C# Task 源代码阅读(2)

摘要:
=null)&&,“Task.ScheduleAndStart():Expected_contingntProperties.m_exceptionsHoldertexists”+“并且记录了错误。”);m_应急属性。m_ exceptionsHolder。MarkAsHandled;}//通过异常包装任务调度异常。throwtse;}}首先,进行合同参数认证,然后保护价值判断。我们希望看到的是AddToActiveTasks的方法。注意,在它之前有一个判断_只有当asyncDebuggingEnabled为true时,才会执行它。当然,默认值为false=null,“NullTaskobjectscan'tbeadedtoActiveTaskscollection”);lock{s_currentActiveTasks[task.Id]=任务;}//始终返回true以将签名保持为向后兼容返回true;}这就是刚性。我们需要将任务对象放入字典中。放进去的目的是什么?当然,它便于查询和管理。此方法处于正常过程中

上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler.

这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndStart(true) 这个方法。接着看这个方

 [SecuritySafeCritical] // Needed for QueueTask
        internal void ScheduleAndStart(bool needsProtection)
        {
            Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
            Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");

            // Set the TASK_STATE_STARTED bit
            if (needsProtection)
            {
                if (!MarkStarted())
                {
                    // A cancel has snuck in before we could get started.  Quietly exit.
                    return;
                }
            }
            else
            {
                m_stateFlags |= TASK_STATE_STARTED;
            }

            if (s_asyncDebuggingEnabled)
            {
                AddToActiveTasks(this);
            }

            if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
            {
                //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
                AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, 0);
            }


            try
            {
                // Queue to the indicated scheduler.
                m_taskScheduler.InternalQueueTask(this);
            }
            catch (ThreadAbortException tae)
            {
                AddException(tae);
                FinishThreadAbortedTask(true, false);
            }
            catch (Exception e)
            {
                // The scheduler had a problem queueing this task.  Record the exception, leaving this task in
                // a Faulted state.
                TaskSchedulerException tse = new TaskSchedulerException(e);
                AddException(tse);
                Finish(false);

                // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
                // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
                // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
                // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.

                if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
                {
                    // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
                    Contract.Assert(
                        (m_contingentProperties != null) &&
                        (m_contingentProperties.m_exceptionsHolder != null) &&
                        (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
                            "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
                            "and to have faults recorded.");

                    m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
                }
                // re-throw the exception wrapped as a TaskSchedulerException.
                throw tse;
            }
        }

开始先做契约参数认证,接着保护数值判断。我们要看的是AddToActiveTasks(this)这个方法,注意在他之前有个判断,在s_asyncDebuggingEnabled 为true 的情况才会执行,当然默认的是false。

  // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
        // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
        private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
 [FriendAccessAllowed]
        internal static bool AddToActiveTasks(Task task)
        {
            Contract.Requires(task != null, "Null Task objects can't be added to the ActiveTasks collection");
            lock (s_activeTasksLock)
            {
                s_currentActiveTasks[task.Id] = task;
            }
            //always return true to keep signature as bool for backwards compatibility
            return true;
        }

这个就是僵我们要执行task 对象放入一个字典中,放入的目的是做什么呢?当然就是为何方便查询和管理。这个方法在正常流程是不会执行的。这里觉得有些奇怪的写法,Task 类里面有个静态静态字典,用于存放自己执行的类集合。当然说到管理和查询,断然我是不会放在这个类,令起新类也好。

这里的代码方法参数验证都是采用契约验证,其实我个人并不是很赞同这东西,虽然C++也有这个。我倒更希望是原本的异常抛出,或者日志记录,或者其他自定义方式。

接着看核心方法 m_taskScheduler.InternalQueueTask(this); 前面我们已经看到默认的m_taskScheduler为ThreadPoolTaskScheduler。接着看代码

 [SecurityCritical]
        internal void InternalQueueTask(Task task)
        {
            Contract.Requires(task != null);

            task.FireTaskScheduledIfNeeded(this);

            this.QueueTask(task);
        }
 [SecurityCritical]
        protected internal override void QueueTask(Task task)
        {
            if ((task.Options & TaskCreationOptions.LongRunning) != 0)
            {
                // Run LongRunning tasks on their own dedicated thread.
                Thread thread = new Thread(s_longRunningThreadWork);
                thread.IsBackground = true; // Keep this thread from blocking process shutdown
                thread.Start(task);
            }
            else
            {
                // Normal handling for non-LongRunning tasks.
                bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);
                ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
            }
        }

现在为止就开始清晰明朗了,看到QueueTask 方法,我已经可以看到task 对象已经传到Threadpool 里面了。至此,可以说到task 一般都是在ThreadPool 里面运行。接着我们再看ThreadpoolTaskScheduler让几个重要的方法

  [SecurityCritical]
        protected internal override bool TryDequeue(Task task)
        {
            // just delegate to TP
            return ThreadPool.TryPopCustomWorkItem(task);
        }

        [SecurityCritical]
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return FilterTasksFromWorkItems(ThreadPool.GetQueuedWorkItems());
        }

        /// <summary>
        /// This internal function will do this:
        ///   (1) If the task had previously been queued, attempt to pop it and return false if that fails.
        ///   (2) Propagate the return value from Task.ExecuteEntry() back to the caller.
        /// 
        /// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs
        /// to account for exceptions that need to be propagated, and throw themselves accordingly.
        /// </summary>
        [SecurityCritical]
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {

        -----------------------
        }
这里有GetScheduledTasks()方法,这个方法就是用来获得当前的Task的,对于去珍断task 的运行状态非常有帮助。至此。我们一步一步看到Task 是如何运行的,当然到达Theadpool可以继续看下去。注意了ThreadPoolTaskScheduler 的访问修饰符是internal sealed,所以在用task 的时候无法用到他,还有里面的方法访问修饰符都是protected 的。到此,我们正常来运行task,还是没法获得到task的本身运行状态。很多人在代码中为了实现某个功能都会大量的使用task,每个人的写法有不一样,task 运行是否成功,是否发生异常 对于整个项目的运行至关重要。那么如何管理,如何查看task 的运行状态呢,在C# code 我们如果想把task 的异常接管到主线程种,必须task wait,但是很多task 都是无需直到返回结果,但是实际上我们还是要关心他的运行状态,那么如何来做,如何来看呢。
1.常规做法,鉴于很多人喜欢TaskFactory.StartNew() 这个写法,所以想把所有的task的加入到一个队列中比较麻烦,因为启动task 的写法很多。所以各自的task的里面自己处理异常,写好日志。
2.使用TaskScheduler,看代码的目的除了了解运行过程,更加了解如和使用这个类,我们只需要写上自己的TaskScheduler,当然继承这个类,是需要实现某些必须方法的,不管是task的start还是TaskFactory的StartNew方法,我们都可以注入自己的TaskScheduler,这样正如TaskScheduler设计初衷一样,所有的task 运行都会交给他来管理,默认的ThreadPoolTaskScheduler是没法使用的(访问修饰符),除非采用一些其他手段,这里不多介绍。所以只能自己重新去实现这个类的相关细节。

免责声明:文章转载自《C# Task 源代码阅读(2)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇1.docker简介及安装用一个实际例子理解Docker volume工作原理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

C#基础系列过滤器与特性

过滤器和特性结合在一起,在方法上优雅地使用过滤器。3.在过滤器中,。NETFrameWork提供了两种类型:一种是提供给ASP的筛选器。NETMVC在命名空间下使用System.Web。另一个是提供给ASP的过滤器。NETWebApi在命名空间下使用System.Web.Http.Filters。这两种类型不能混合使用,否则无法拦截并生效。...

Cesium深入浅出之视频投影【转】

通常,我们使用矩形,因为视频形状是方形的。据怀疑,视频标签隐藏了这段关系。如果再次显示,视频将再次移动。此处使用VideoSynchronizer。它可以使视频元素与铯的模拟时钟同步。让我们看看它的构造函数:name type description optionsObject option子属性:name type默认值description用于驱动视频的...

windows 常用命令行操作

目录操作˃pwd打印当前工作目录,通过此关键词可以查看当前所处的路径˃cd更改目录,用于多个目录之间的切换具体输入:cd目录名cd目录名/子目录名(可通过此方式到达最底层的目录)cd~(返回home目录)cd..(返回上一级目录)cd../..(返回上两级目录)cd盘符名:(不同盘符间跳转,cd后面跟上路径则可实现精准跳转)˃mkdir创建目录具体输入:mk...

axios 学习文档

Axios是一个基于承诺的HTTP库,可以在浏览器和node.js中使用。执行POST请求axis.POST.then。接住执行多个并发请求函数getUserAccount(){returnaxios.get;}函数getUserPermissions(){returnaxios.get;}全部承诺。然后axios API可以通过传递相关配置来请求axios...

iReport制作报表,字数过多换行问题

1.当字段中显示的数据太长而无法放入表中时,需要自动换行。选择要更改的表(显示动态内容的字段),并将Stretchwithoverflow属性设置为选中。未选中前:选中后:2.然而,桌子坏了,非常难看。此时,我们需要设置一个属性,使同一行中的其他字段保持与换行字段相同的高度。此时,我们需要框选要显示在整行中的动态字段和表;将属性StretchType设置为R...

Winform知识点

BringToFront()将控件移动到Z顺序的前面。...