日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Task揭秘(一)

發布時間:2023/12/4 asp.net 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Task揭秘(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Task為.NET提供了基于任務的異步模式,它不是線程,它運行在線程池的線程上。本著開源的精神, 本文以解讀基于.NET4.5 Task源碼的方式來揭秘Task的實現原理。

?

Task的創建

Task的創建方式主要有2種:Task.Run?和Task.Factory.StartNew,各自有不同的overload,這里只解讀其中的一種方式,其他有興趣的請自行解讀。

先來看看Task.Run源碼:

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

調用了Task.InternalStartNew,第一個參數為null,并傳入TaskScheduler.Default和TaskCreationOptions.DenyChildAttach.

再來看看Task.Factory.StartNew源碼:

public Task StartNew(Action<object> action, object state, CancellationToken cancellationToken)

{

StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

Task internalCurrent = Task.InternalCurrent;

return Task.InternalStartNew(internalCurrent, (Delegate) action, state, cancellationToken, this.GetDefaultScheduler(internalCurrent), this.m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);

}

也是調用Task.InternalStartNew,第一個參數為internalCurrent,當前為null,并傳入GetDefaultScheduler(internalCurrent)m_defaultCreationOptions


private TaskScheduler GetDefaultScheduler(Task currTask)

{

if (this.m_defaultScheduler != null)

return this.m_defaultScheduler;

if (currTask != null && (currTask.CreationOptions & TaskCreationOptions.HideScheduler) == TaskCreationOptions.None)

return currTask.ExecutingTaskScheduler;

return TaskScheduler.Default;

}

如果internalCurrent不為空而且options是TaskCreationOptions.HideScheduler,那么啟用internalCurrent的TaskScheduler??上?/span>internalCurrent為null,所以啟用默認的TaskScheduler,跟入代碼發現默認的TaskScheduler是ThreadPoolTaskScheduler,看名字就知道用的是線程池的任務調度,跟“黑盒”傳說的一樣的。m_defaultCreationOptions在Task.Factory的默認無參構造函數里被賦值TaskCreationOptions.None。

public abstract class TaskScheduler

{

private static readonly ConditionalWeakTable<TaskScheduler, object> s_activeTaskSchedulers = new ConditionalWeakTable<TaskScheduler, object>();

private static readonly TaskScheduler s_defaultTaskScheduler = (TaskScheduler) new ThreadPoolTaskScheduler();

...

}

目前來看兩個方法最大的區別在于TaskCreationOption的不同,一個是DenyChildAttach,另一個是None。

接著往下看InternalStartNew:

internal static Task InternalStartNew(Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)

{

if (scheduler == null)

throw new ArgumentNullException("scheduler");

Task task = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);

task.PossiblyCaptureContext(ref stackMark);

task.ScheduleAndStart(false);

return task;

}

首先實例化一個Task:

internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)

{

if (action == null)

throw new ArgumentNullException("action");

if ((creationOptions & TaskCreationOptions.AttachedToParent) != TaskCreationOptions.None || (internalOptions & InternalTaskOptions.SelfReplicating) != InternalTaskOptions.None)

this.m_parent = parent;

this.TaskConstructorCore((object) action, state, cancellationToken, creationOptions, internalOptions, scheduler);

}

如果option是AttachToParent,那么internalCurrent就賦值給m_parent,目前為null,SelfReplicating是用來做并行計算的,會在TPL里詳解。隨后調用TaskConstructorCore

internal void TaskConstructorCore(object action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)

{

this.m_action = action;

this.m_stateObject = state;

this.m_taskScheduler = scheduler;

if ((creationOptions & ~(TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent | TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously)) != TaskCreationOptions.None)

throw new ArgumentOutOfRangeException("creationOptions");

if ((creationOptions & TaskCreationOptions.LongRunning) != TaskCreationOptions.None && (internalOptions & InternalTaskOptions.SelfReplicating) != InternalTaskOptions.None)

throw new InvalidOperationException(Environment.GetResourceString("Task_ctor_LRandSR"));

int num = (int) (creationOptions | (TaskCreationOptions) internalOptions);

if (this.m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != InternalTaskOptions.None)

num |= 33554432;

this.m_stateFlags = num;

if (this.m_parent != null && (creationOptions & TaskCreationOptions.AttachedToParent) != TaskCreationOptions.None && (this.m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == TaskCreationOptions.None)

this.m_parent.AddNewChild();

if (!cancellationToken.CanBeCanceled)

return;

this.AssignCancellationToken(cancellationToken, (Task) null, (TaskContinuation) null);

}

如果options不為DenyChildAttach而且m_parent不為空,則把當前task作為child添加到m_parent。也就是說Task.Run不允許把要執行的task作為當前task的child。

Task已創建,接著調用PossiblyCaptureContext來獲取execution context。

internal static ExecutionContext Capture(ref StackCrawlMark stackMark, ExecutionContext.CaptureOptions options)

{

ExecutionContext.Reader executionContextReader = Thread.CurrentThread.GetExecutionContextReader();

if (executionContextReader.IsFlowSuppressed)

return (ExecutionContext) null;

SecurityContext securityContext = SecurityContext.Capture(executionContextReader, ref stackMark);

HostExecutionContext executionContext1 = HostExecutionContextManager.CaptureHostExecutionContext();

SynchronizationContext synchronizationContext = (SynchronizationContext) null;

LogicalCallContext logicalCallContext = (LogicalCallContext) null;

if (!executionContextReader.IsNull)

{

if ((options & ExecutionContext.CaptureOptions.IgnoreSyncCtx) == ExecutionContext.CaptureOptions.None)

synchronizationContext = executionContextReader.SynchronizationContext == null ? (SynchronizationContext) null : executionContextReader.SynchronizationContext.CreateCopy();

if (executionContextReader.LogicalCallContext.HasInfo)

logicalCallContext = executionContextReader.LogicalCallContext.Clone();

}

Dictionary<IAsyncLocal, object> dictionary = (Dictionary<IAsyncLocal, object>) null;

List<IAsyncLocal> asyncLocalList = (List<IAsyncLocal>) null;

if (!executionContextReader.IsNull)

{

dictionary = executionContextReader.DangerousGetRawExecutionContext()._localValues;

asyncLocalList = executionContextReader.DangerousGetRawExecutionContext()._localChangeNotifications;

}

if ((options & ExecutionContext.CaptureOptions.OptimizeDefaultCase) != ExecutionContext.CaptureOptions.None && securityContext == null && (executionContext1 == null && synchronizationContext == null) && ((logicalCallContext == null || !logicalCallContext.HasInfo) && (dictionary == null && asyncLocalList == null)))

return ExecutionContext.s_dummyDefaultEC;

ExecutionContext executionContext2 = new ExecutionContext();

executionContext2.SecurityContext = securityContext;

if (executionContext2.SecurityContext != null)

executionContext2.SecurityContext.ExecutionContext = executionContext2;

executionContext2._hostExecutionContext = executionContext1;

executionContext2._syncContext = synchronizationContext;

executionContext2.LogicalCallContext = logicalCallContext;

executionContext2._localValues = dictionary;

executionContext2._localChangeNotifications = asyncLocalList;

executionContext2.isNewCapture = true;

return executionContext2;

}

ExecutionContext包含了SecurityContext,SynchronizationContext以及LogicalCallContext,其中SynchronizationContext需要做CreateCopy,LogicalCallContext需要做clone,所有這一切都是用戶態的,不涉及內核,性能棒棒噠!

接著調用ScheduleAndStart:

internal void ScheduleAndStart(bool needsProtection)

{

if (needsProtection)

{

if (!this.MarkStarted())

return;

}

else

this.m_stateFlags = this.m_stateFlags | 65536;

if (Task.s_asyncDebuggingEnabled)

Task.AddToActiveTasks(this);

if (AsyncCausalityTracer.LoggingOn && (this.Options & (TaskCreationOptions) 512) == TaskCreationOptions.None)

AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: " + ((Delegate) this.m_action).Method.Name, 0UL);

try

{

this.m_taskScheduler.InternalQueueTask(this);

}

catch (ThreadAbortException ex)

{

this.AddException((object) ex);

this.FinishThreadAbortedTask(true, false);

}

catch (System.Exception ex)

{

TaskSchedulerException schedulerException = new TaskSchedulerException(ex);

this.AddException((object) schedulerException);

this.Finish(false);

if ((this.Options & (TaskCreationOptions) 512) == TaskCreationOptions.None)

this.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);

throw schedulerException;

}

}

?

internal void InternalQueueTask(Task task)

{

task.FireTaskScheduledIfNeeded(this);

this.QueueTask(task);

}

FireTaskScheduledIfNeeded判斷是否開啟EWT Trace,接著調用ThreadPoolTaskScheduler.QueueTask。


private static readonly ParameterizedThreadStart s_longRunningThreadWork = new ParameterizedThreadStart(ThreadPoolTaskScheduler.LongRunningThreadWork);

private static void LongRunningThreadWork(object obj)

{

(obj as Task).ExecuteEntry(false);

}

protected internal override void QueueTask(Task task)

{

if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)

{

new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)

{

IsBackground = true

}.Start((object) task);

}

else

{

bool forceGlobal = (uint) (task.Options & TaskCreationOptions.PreferFairness) > 0U;

ThreadPool.UnsafeQueueCustomWorkItem((IThreadPoolWorkItem) task, forceGlobal);

}

}



如果options是LongRunning,那么單獨創建一個線程執行該任務(ExecuteEntry),否則就調用ThreadPool.UnsafeQueueCustomWorkItem,這個方法我們熟,還記得在.net線程池內幕里有講到的global work queue和local work queue嗎?給ThreadPool添加一個任務實際上是在global work queue添加一個任務,而task就是往local work queue里添加任務。

ThreadPoolWorkQueue源碼:

public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)

{

ThreadPoolWorkQueueThreadLocals queueThreadLocals = (ThreadPoolWorkQueueThreadLocals) null;

if (!forceGlobal)

queueThreadLocals = ThreadPoolWorkQueueThreadLocals.threadLocals;

if (this.loggingEnabled)

FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject((object) callback);

if (queueThreadLocals != null)

{

queueThreadLocals.workStealingQueue.LocalPush(callback);

}

else

{

ThreadPoolWorkQueue.QueueSegment comparand = this.queueHead;

while (!comparand.TryEnqueue(callback))

{

Interlocked.CompareExchange<ThreadPoolWorkQueue.QueueSegment>(ref comparand.Next, new ThreadPoolWorkQueue.QueueSegment(), (ThreadPoolWorkQueue.QueueSegment) null);

for (; comparand.Next != null; comparand = this.queueHead)

Interlocked.CompareExchange<ThreadPoolWorkQueue.QueueSegment>(ref this.queueHead, comparand.Next, comparand);

}

}

this.EnsureThreadRequested();

}

由于線程已經執行過任務(global的也有可能是local的),所以代碼會走到queueThreadLocals.workStealingQueue.LocalPush(callback)


internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[32];

private SpinLock m_foreignLock = new SpinLock(false);

public void LocalPush(IThreadPoolWorkItem obj)

{

int num1 = this.m_tailIndex;

if (num1 == int.MaxValue)

{

bool lockTaken = false;

try

{

this.m_foreignLock.Enter(ref lockTaken);

if (this.m_tailIndex == int.MaxValue)

{

this.m_headIndex = this.m_headIndex & this.m_mask;

this.m_tailIndex = num1 = this.m_tailIndex & this.m_mask;

}

}

finally

{

if (lockTaken)

this.m_foreignLock.Exit(true);

}

}

if (num1 < this.m_headIndex + this.m_mask)

{

Volatile.Write<IThreadPoolWorkItem>(ref this.m_array[num1 & this.m_mask], obj);

this.m_tailIndex = num1 + 1;

}

else

{

bool lockTaken = false;

try

{

this.m_foreignLock.Enter(ref lockTaken);

int num2 = this.m_headIndex;

int num3 = this.m_tailIndex - this.m_headIndex;

if (num3 >= this.m_mask)

{

IThreadPoolWorkItem[] threadPoolWorkItemArray = new IThreadPoolWorkItem[this.m_array.Length << 1];

for (int index = 0; index < this.m_array.Length; ++index)

threadPoolWorkItemArray[index] = this.m_array[index + num2 & this.m_mask];

this.m_array = threadPoolWorkItemArray;

this.m_headIndex = 0;

this.m_tailIndex = num1 = num3;

this.m_mask = this.m_mask << 1 | 1;

}

Volatile.Write<IThreadPoolWorkItem>(ref this.m_array[num1 & this.m_mask], obj);

this.m_tailIndex = num1 + 1;

}

finally

{

if (lockTaken)

this.m_foreignLock.Exit(false);

}

}

}

Local work queue(m_array)首先被限死為32,如果queue超過最大數了,則擴大為原來的2倍,以此類推。這里也使用了自旋鎖和內存寫屏障來代替同步鎖提高性能。

?

至此,task已被創建好,并加入到了ThreadPool的local work queue。那么task是如何被調度的呢?為什么LongRunning就要單獨起一個線程去做?請聽下回分解!


原文地址: http://www.cnblogs.com/newbier/p/6203422.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的.NET Task揭秘(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。