今天我們透過查看 task 的 constructer 來理解一個 Task 的主要任務完成過程。
有很多種多載, 只取其中一種, 之後會細說這個 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
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
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 執行 , 以及連續任務區如何開始處理
明天見 !