iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0
自我挑戰組

從C到JS的同步非同步探索系列 第 12

[Day12] .Net Task 底層(5)

  • 分享至 

  • xImage
  •  

前言

今天我們透過查看 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 執行 , 以及連續任務區如何開始處理

明天見 !


上一篇
[Day 11] .Net Task 底層(4)
下一篇
[Day 13] .Net Task 底層(6)
系列文
從C到JS的同步非同步探索30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言