[Day12] .Net Task 底层(5)

前言

今天我们透过查看 task 的 constructer 来理解一个 Task 的主要任务完成过程。

Task's constructer

有很多种多载, 只取其中一种, 之後会细说这个 case 的全部流程

/// <summary>
/// An internal constructor used by the factory methods on task and its descendent(s).
/// This variant does not capture the ExecutionContext; it is up to the caller to do that.
/// </summary>
/// <param name="action">An action to execute.</param>
/// <param name="state">Optional state to pass to the action.</param>
/// <param name="parent">Parent of Task.</param>
/// <param name="cancellationToken">A CancellationToken for the task.</param>
/// <param name="scheduler">A task scheduler under which the task will run.</param>
/// <param name="creationOptions">Options to control its execution.</param>
/// <param name="internalOptions">Internal options to control its execution</param>
internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken,
    TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    Contract.EndContractBlock();

    // This is readonly, and so must be set in the constructor
    // Keep a link to your parent if: (A) You are attached, or (B) you are self-replicating.
    if (((creationOptions & TaskCreationOptions.AttachedToParent) != 0) ||
        ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
        )
    {
        m_parent = parent;
    }

    TaskConstructorCore(action, state, cancellationToken, creationOptions, internalOptions, scheduler);
}

掠过上方设置部分, 直接查看 TaskConstructorCore(action, state, cancellationToken, creationOptions, internalOptions, scheduler);

/// <summary>
/// Common logic used by the following internal ctors:
///     Task()
///     Task(object action, object state, Task parent, TaskCreationOptions options, TaskScheduler taskScheduler)
/// </summary>
/// <param name="action">Action for task to execute.</param>
/// <param name="state">Object to which to pass to action (may be null)</param>
/// <param name="scheduler">Task scheduler on which to run thread (only used by continuation tasks).</param>
/// <param name="cancellationToken">A CancellationToken for the Task.</param>
/// <param name="creationOptions">Options to customize behavior of Task.</param>
/// <param name="internalOptions">Internal options to customize behavior of Task.</param>
internal void TaskConstructorCore(object action, object state, CancellationToken cancellationToken,
    TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
{
    m_action = action;
    m_stateObject = state;
    m_taskScheduler = scheduler;

    // Check for validity of options
    if ((creationOptions &
            ~(TaskCreationOptions.AttachedToParent |
              TaskCreationOptions.LongRunning |
              TaskCreationOptions.DenyChildAttach |
              TaskCreationOptions.HideScheduler |
              TaskCreationOptions.PreferFairness |
              TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
    {
        throw new ArgumentOutOfRangeException("creationOptions");
    }

#if DEBUG
            // Check the validity of internalOptions
            int illegalInternalOptions = 
                    (int) (internalOptions &
                            ~(InternalTaskOptions.SelfReplicating |
                              InternalTaskOptions.ChildReplica |
                              InternalTaskOptions.PromiseTask |
                              InternalTaskOptions.ContinuationTask |
                              InternalTaskOptions.LazyCancellation |
                              InternalTaskOptions.QueuedByRuntime));
            Contract.Assert(illegalInternalOptions == 0, "TaskConstructorCore: Illegal internal options");
#endif

    // Throw exception if the user specifies both LongRunning and SelfReplicating
    if (((creationOptions & TaskCreationOptions.LongRunning) != 0) &&
        ((internalOptions & InternalTaskOptions.SelfReplicating) != 0))
    {
        throw new InvalidOperationException(Environment.GetResourceString("Task_ctor_LRandSR"));
    }

    // Assign options to m_stateAndOptionsFlag.
    Contract.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags");
    Contract.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits");
    var tmpFlags = (int)creationOptions | (int)internalOptions;
    if ((m_action == null) || ((internalOptions & InternalTaskOptions.ContinuationTask) != 0))
    {
        // For continuation tasks or TaskCompletionSource.Tasks, begin life in the 
        // WaitingForActivation state rather than the Created state.
        tmpFlags |= TASK_STATE_WAITINGFORACTIVATION;
    }
    m_stateFlags = tmpFlags; // one write to the volatile m_stateFlags instead of two when setting the above options

    // Now is the time to add the new task to the children list 
    // of the creating task if the options call for it.
    // We can safely call the creator task's AddNewChild() method to register it, 
    // because at this point we are already on its thread of execution.

    if (m_parent != null
        && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
        && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
        )
    {
        m_parent.AddNewChild();
    }

    // if we have a non-null cancellationToken, allocate the contingent properties to save it
    // we need to do this as the very last thing in the construction path, because the CT registration could modify m_stateFlags
    if (cancellationToken.CanBeCanceled)
    {
        Contract.Assert((internalOptions &
            (InternalTaskOptions.ChildReplica | InternalTaskOptions.SelfReplicating | InternalTaskOptions.ContinuationTask)) == 0,
            "TaskConstructorCore: Did not expect to see cancelable token for replica/replicating or continuation task.");

        AssignCancellationToken(cancellationToken, null, null);
    }
}

跳过错误处理。

可以发现就是把, action (该任务要做的行为) , 以及 scheduler (要利用何种排程方式), 存入 task 的资料结构。

注 : 假设没有父任务 ( m_parent == null) , 及任务状态使用预设 ( status == null )

接着我们来看看谁会调用这个 constructor

Task.Run

public static Task Run(Action action, CancellationToken cancellationToken)
{
    StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
    return Task.InternalStartNew(null, action, null, cancellationToken, TaskScheduler.Default,
        TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}

这也是 C# 常用的非同步语法

https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.run?view=net-5.0

可以发现调用後他就会把, 要做的事(action) , 和排程器 (TaskScheduler.Default ) , 送入InternalStartNew

这边说一下 TaskScheduler.Default 其实就是 threadpool , 所以其实, action 最後也会被存入 TP

再来看看 InternalStartNew

InternalStartNew

internal static Task InternalStartNew(
            Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
            TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
{
    // Validate arguments.
    if (scheduler == null)
    {
        throw new ArgumentNullException("scheduler");
    }
    Contract.EndContractBlock();

    // Create and schedule the task. This throws an InvalidOperationException if already shut down.
    // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
    Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
    t.PossiblyCaptureContext(ref stackMark);

    t.ScheduleAndStart(false);
    return t;
}

其创建了一个我们最前面提到的 constructor , 他会把 action 和 scheduler 存入

之後会调用这两个方法

t.PossiblyCaptureContext(ref stackMark);
t.ScheduleAndStart(false);

我们明天来看看他们在干嘛吧 !

明天进度

探索 action 如何被放入 TP 执行 , 以及连续任务区如何开始处理

明天见 !


<<:  Day5-"台风天还是要练习吧"

>>:  Day.3 「建构网页的基石!」 —— 使用网页标签

第39天-学习 od 指令

进度 : 鸟哥的 Linux 私房菜 -- 第六章、Linux 档案与目录管理 快速查询对应 ASC...

Day 6: 人工智慧在音乐领域的应用 (AI发展史与简介 - 一战封神AlphaGo)

昨天我们聊到了AI在1987-1993年进入了第二次寒冬,而这段期间里AI成了过街老鼠人人喊打,导致...

[鼠年全马] W36 - Vue出一个旅馆预约平台(10)

转眼间来到了 Vue出旅馆预约平台 第10篇了...做的真久阿... 还好不是公司的专案, 不然绝对...

[前端暴龙机,Vue2.x 进化 Vue3 ] Day10.条件渲染

当我们有区分,在某种条件下看到的会是 A 画面,某一些条件下看到的会是 B 画面...的情形,例如每...

Day 18 - 产业研究分析浅谈

转换一下, 来谈谈PM的日常, 还有其他工作类型, 例如像是产业研究分析的任务, 尤其是针对想要选...