您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么使用c#異步操作async await狀態機”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
大家應該都比較清楚async和await
這對關鍵字是一組語法糖,關于語法糖大家可以理解為,編碼過程中寫了一個關鍵字,但是編譯的時候會把它編譯成別的東西,主要是用來提升開發效率。比如我有一段關于async和await
相關的代碼,如下所示
var taskOne = await TaskOne(); Console.WriteLine(taskOne); Console.ReadLine(); static async Task<string> TaskOne() { var httpResponse = await ClassFactory.Client.GetAsync("https://www.cnblogs.com"); var content = await httpResponse.Content.ReadAsStringAsync(); return content; } public class ClassFactory { public static HttpClient Client = new HttpClient(); }
這段代碼是基于c#頂級語句聲明的,它是缺省Main方法的,不過在編譯的時候編譯器會幫我們補齊Main方法,因為執行的時候JIT
需要Main方法作為執行入口。關于如何查看編譯后的代碼。我經常使用的是兩個工具,分別是ILSpy
和dnSpy
。這倆工具的區別在于ILSpy
生成的代碼更清晰,dnSpy
生成的源碼是可以直接調試的。需要注意的是如果使用的是ILSpy
如果查看語法糖本質的話,需要在ILSpy
上選擇比語法糖版本低的版本,比如c# async和await關鍵字是在c# 5.0版本中引入的,所以我們這里我們在ILSpy
里需要選擇c#4.0或以下版本,入下圖所示
如果使用的是dnSpy
的話,需要在調試-->選項-->反編譯器
中設置相關選項,如下所示
這樣就可以看到編譯后生成的代碼了。
圍繞上面的示例我這里使用的Debug模式
下編譯生成的dll使用的ILSpy
進行反編譯,因為這里我需要讓編譯的源碼看起來更清晰一點,而不是調試。如下所示首先看Main方法
//因為我們上面代碼var taskOne = await TaskOne() //使用了await語法糖,所以被替換成了狀態機調用 [AsyncStateMachine(typeof(<<Main>$>d__0))] [DebuggerStepThrough] private static Task <Main>$(string[] args) { //創建狀態機實例 <<Main>$>d__0 stateMachine = new <<Main>$>d__0(); stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create(); stateMachine.args = args; //設置狀態-1 stateMachine.<>1__state = -1; //啟動狀態機 stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } //這是系統默認幫我們生成的static void Main的入口方法 [SpecialName] [DebuggerStepThrough] private static void <Main>(string[] args) { //同步調用<Main>$方法 <Main>$(args).GetAwaiter().GetResult(); }
上面的代碼就是編譯器為我們生成的Main
方法,通過這里我們可以得到兩條信息
頂級語句
編譯器會幫我們生成固定的入口函數格式,即static void Main
這種標準格式
編譯器遇到await
關鍵字則會編譯出一段狀態機相關的代碼,把我們的邏輯放到編譯的狀態機類里
通過上面我們可以看到<<Main>$>d__0
這個類是編譯器幫我們生成的,我們可以看一下生成的代碼
[CompilerGenerated] private sealed class <<Main>$>d__0 : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder <>t__builder; public string[] args; private string <taskOne>5__1; private string <>s__2; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] private TaskAwaiter<string> <>u__1; private void MoveNext() { int num = <>1__state; try { TaskAwaiter<string> awaiter; //num的值來自<>1__state,由于在創建狀態機的時候傳遞的是-1所以一定會走到這個邏輯 if (num != 0) { //調用TaskOne方法,也就是上面我們寫的業務方法 //這個方法返回的是TaskAwaiter<>實例,以為我們TaskOne方法是異步方法 awaiter = <<Main>$>g__TaskOne|0_0().GetAwaiter(); //判斷任務是否執行完成 if (!awaiter.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter; <<Main>$>d__0 stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } } else { awaiter = <>u__1; <>u__1 = default(TaskAwaiter<string>); num = (<>1__state = -1); } //調用GetResult()方法獲取異步執行結果 <>s__2 = awaiter.GetResult(); <taskOne>5__1 = <>s__2; <>s__2 = null; //這里對應我們上面的輸出調用TaskOne方法的結果 Console.WriteLine(<taskOne>5__1); Console.ReadLine(); } catch (Exception exception) { <>1__state = -2; <taskOne>5__1 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <taskOne>5__1 = null; <>t__builder.SetResult(); } void IAsyncStateMachine.MoveNext() { this.MoveNext(); } [DebuggerHidden] private void SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine) { this.SetStateMachine(stateMachine); } }
這里的代碼可以看到編譯器生成的代碼,其實這就是對應上面我們寫的代碼
var taskOne = await TaskOne(); Console.WriteLine(taskOne); Console.ReadLine();
因為我們使用了await
關鍵字,所以它幫我們生成了IAsyncStateMachine
類,里面的核心邏輯咱們待會在介紹,因為今天的主題TaskOne
方法還沒介紹完成呢,TaskOne
生成的代碼如下所示
//TaskOne方法編譯時生成的代碼 [CompilerGenerated] private sealed class <<<Main>$>g__TaskOne|0_0>d : IAsyncStateMachine { public int <>1__state; public AsyncTaskMethodBuilder<string> <>t__builder; private HttpResponseMessage <httpResponse>5__1; private string <content>5__2; private HttpResponseMessage <>s__3; private string <>s__4; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] private TaskAwaiter<HttpResponseMessage> <>u__1; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] private TaskAwaiter<string> <>u__2; private void MoveNext() { int num = <>1__state; string result; try { //因為我們使用了兩次await所以這里會有兩個TaskAwaiter<>實例 //var httpResponse = await ClassFactory.Client.GetAsync("https://www.cnblogs.com"); //var content = await httpResponse.Content.ReadAsStringAsync(); TaskAwaiter<string> awaiter; TaskAwaiter<HttpResponseMessage> awaiter2; if (num != 0) { if (num == 1) { awaiter = <>u__2; <>u__2 = default(TaskAwaiter<string>); num = (<>1__state = -1); goto IL_0100; } //這段邏輯針對的是我們手寫的這段代碼 //await ClassFactory.Client.GetAsync("https://www.cnblogs.com") awaiter2 = ClassFactory.Client.GetAsync("https://www.cnblogs.com").GetAwaiter(); //判斷任務是否完成 if (!awaiter2.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter2; <<<Main>$>g__TaskOne|0_0>d stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } } else { awaiter2 = <>u__1; <>u__1 = default(TaskAwaiter<HttpResponseMessage>); num = (<>1__state = -1); } //同步獲取HttpResponseMessage結果實例 <>s__3 = awaiter2.GetResult(); <httpResponse>5__1 = <>s__3; <>s__3 = null; //這段代碼對應生成的則是await httpResponse.Content.ReadAsStringAsync() awaiter = <httpResponse>5__1.Content.ReadAsStringAsync().GetAwaiter(); if (!awaiter.IsCompleted) { num = (<>1__state = 1); <>u__2 = awaiter; <<<Main>$>g__TaskOne|0_0>d stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } goto IL_0100; IL_0100: //同步獲取httpResponse.Content.ReadAsStringAsync()放的結果 <>s__4 = awaiter.GetResult(); <content>5__2 = <>s__4; <>s__4 = null; result = <content>5__2; } catch (Exception exception) { <>1__state = -2; <httpResponse>5__1 = null; <content>5__2 = null; <>t__builder.SetException(exception); return; } <>1__state = -2; <httpResponse>5__1 = null; <content>5__2 = null; //調用AsyncTaskMethodBuilder<>方法放置httpResponse.Content.ReadAsStringAsync()結果 <>t__builder.SetResult(result); } void IAsyncStateMachine.MoveNext() { this.MoveNext(); } [DebuggerHidden] private void SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine) { } void IAsyncStateMachine.SetStateMachine([System.Runtime.CompilerServices.Nullable(1)] IAsyncStateMachine stateMachine) { this.SetStateMachine(stateMachine); } }
到這里為止,這些方法就是編譯器幫我們生成的代碼,也就是這些代碼就在生成好的dll
里的。
接下來我們分析一下狀態機的調用過程,回到上面的stateMachine.<>t__builder.Start(ref stateMachine)
這段狀態機啟動代碼,我們跟進去看一下里面的邏輯
[MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerStepThrough] public void Start<[Nullable(0)] TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { //調用了AsyncMethodBuilderCore的Start方法并傳遞狀態機實例 //即<<Main>$>d__0 stateMachine = new <<Main>$>d__0()實例 AsyncMethodBuilderCore.Start(ref stateMachine); } //AsyncMethodBuilderCore的Start方法 [DebuggerStepThrough] public static void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { if (stateMachine == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine); } //獲取當前線程實例 Thread currentThread = Thread.CurrentThread; //獲取當前執行上下文 ExecutionContext executionContext = currentThread._executionContext; //獲取當前同步上下文 SynchronizationContext synchronizationContext = currentThread._synchronizationContext; try { //調用狀態機的MoveNext方法 stateMachine.MoveNext(); } finally { //執行完MoveNext之后 //還原SynchronizationContext同步上下文到當前實例 if (synchronizationContext != currentThread._synchronizationContext) { currentThread._synchronizationContext = synchronizationContext; } //還原ExecutionContext執行上下文到當前實例 ExecutionContext executionContext2 = currentThread._executionContext; if (executionContext != executionContext2) { //執行完成之后把執行上下文裝載到當前線程 ExecutionContext.RestoreChangedContextToThread(currentThread, executionContext, executionContext2); } } }
執行完異步任務之后,會判斷SynchronizationContext
同步上下文環境和ExecutionContext
執行上下文環境,保證異步異步之后的可以操作UI線程
上的控件,或者異步的后續操作和之前的操作處在相同的執行上線文中。
題外話:ExecutionContext 是一個用于傳遞狀態和環境信息的類,它可以在不同的執行上下文之間傳遞狀態。執行上下文表示代碼執行的環境,包括線程、應用程序域、安全上下文和調用上下文等。ExecutionContext 對象包含當前線程上下文的所有信息,如當前線程的安全上下文、邏輯執行上下文、同步上下文和物理執行上下文等。它提供了方法,可以將當前的執行上下文復制到另一個線程中,或者在異步操作之間保存和還原執行上下文。在異步編程中,使用 ExecutionContext 可以確保代碼在正確的上下文中運行,并且傳遞必要的狀態和環境信息。
SynchronizationContext 是一個用于同步執行上下文和處理 UI 線程消息循環的抽象類。它可以將回調方法派發到正確的線程中執行,避免了跨線程訪問的問題,并提高了應用程序的響應性和可靠性。在異步編程中,可以使用 SynchronizationContext.Current 屬性獲取當前線程的同步上下文,并使用同步上下文的 Post 或 Send 方法將回調方法派發到正確的線程中執行。
由于調用stateMachine.<>t__builder.Start(ref stateMachine)
傳遞的是new <<Main>$>d__0()
實例,所以這里核心就是在調用生成的狀態機IAsyncStateMachine
實例,即我們上面的<<Main>$>d__0
類的MoveNext()
方法
void IAsyncStateMachine.MoveNext() { this.MoveNext(); }
由上面的代碼可知,本質是調用的私有的MoveNext()
方法,即會執行我們真實邏輯的那個方法。由于編譯器生成的狀態機代碼的邏輯是大致相同的,所以我們直接來看,我們業務具體落實的代碼即<<<Main>$>g__TaskOne|0_0>d
狀態機類里的,私有的那個MoveNext
方法代碼
AsyncTaskMethodBuilder<string> <>t__builder; TaskAwaiter<HttpResponseMessage> awaiter2; if (num != 0) { if (num == 1) {} //ClassFactory.Client.GetAsyn()方法生成的邏輯 awaiter2 = ClassFactory.Client.GetAsync("https://www.cnblogs.com").GetAwaiter(); if (!awaiter2.IsCompleted) { num = (<>1__state = 0); <>u__1 = awaiter2; <<<Main>$>g__TaskOne|0_0>d stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } //同步獲取異步結果 <>s__4 = awaiter.GetResult(); } else {} TaskAwaiter<string> awaiter; //httpResponse.Content.ReadAsStringAsync()方法生成的邏輯 awaiter = <httpResponse>5__1.Content.ReadAsStringAsync().GetAwaiter(); //判斷任務是否完成 if (!awaiter.IsCompleted) { num = (<>1__state = 1); <>u__2 = awaiter; <<<Main>$>g__TaskOne|0_0>d stateMachine = this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } //同步獲取異步結果,并將返回值裝載 result= awaiter.GetResult(); <>t__builder.SetResult(result);
當然這里我們省了里面的很多邏輯,為了讓結構看起來更清晰一點。
通過上面的它生成的結構來看,我們寫代碼的時候一個方法里的每個await都會被生成一個TaskAwaiter
邏輯,根據當前異步狀態IsCompleted
判斷任務是否完成,來執行下一步操作。如果任務未完成IsCompleted為false
則調用AsyncTaskMethodBuilder
實例的AwaitUnsafeOnCompleted
方法,如果異步已完成則直接獲取異步結果,進行下一步。
通過上面的邏輯我們可以看到,如果異步任務沒有完成則調用了AsyncTaskMethodBuilder
實例的AwaitUnsafeOnCompleted
方法。接下來我們就看下AwaitUnsafeOnCompleted
方法的實現
public void AwaitUnsafeOnCompleted<[Nullable(0)] TAwaiter, [Nullable(0)] TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { //調用AwaitUnsafeOnCompleted方法 AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); } internal static void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref Task<TResult> taskField) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { //創建IAsyncStateMachineBox實例 IAsyncStateMachineBox stateMachineBox = GetStateMachineBox(ref stateMachine, ref taskField); //調用AwaitUnsafeOnCompleted()方法 AwaitUnsafeOnCompleted(ref awaiter, stateMachineBox); } internal static void AwaitUnsafeOnCompleted<TAwaiter>(ref TAwaiter awaiter, IAsyncStateMachineBox box) where TAwaiter : ICriticalNotifyCompletion { //判斷awaiter實例類型 if (default(TAwaiter) != null && awaiter is ITaskAwaiter) { //獲取TaskAwaiter實例的m_task屬性即Task類型 TaskAwaiter.UnsafeOnCompletedInternal(Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter).m_task, box, true); return; } if (default(TAwaiter) != null && awaiter is IConfiguredTaskAwaiter) { //與上面邏輯一致m_task屬性即Task類型本質他們都在操作Task ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter reference = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter); TaskAwaiter.UnsafeOnCompletedInternal(reference.m_task, box, reference.m_continueOnCapturedContext); return; } if (default(TAwaiter) != null && awaiter is IStateMachineBoxAwareAwaiter) { try { //調用IStateMachineBoxAwareAwaiter實例的AwaitUnsafeOnCompleted方法 ((IStateMachineBoxAwareAwaiter)(object)awaiter).AwaitUnsafeOnCompleted(box); return; } catch (Exception exception) { System.Threading.Tasks.Task.ThrowAsync(exception, null); return; } } try { //調用ICriticalNotifyCompletion實例的UnsafeOnCompleted方法 awaiter.UnsafeOnCompleted(box.MoveNextAction); } catch (Exception exception2) { System.Threading.Tasks.Task.ThrowAsync(exception2, null); } }
通過這個方法我們可以看到傳遞進來的TAwaiter都是ICriticalNotifyCompletion
的實現類,所以他們的行為存在一致性,只是具體的實現動作根據不同的實現類型來判斷。
如果是ITaskAwaiter
類的話直接調用TaskAwaiter.UnsafeOnCompletedInternal()
方法,傳遞了TaskAwaiter.m_task
屬性,這是一個Task
類型的屬性
如果是IConfiguredTaskAwaiter
類型的話,也是調用了TaskAwaiter.UnsafeOnCompletedInternal()
方法,傳遞了ConfiguredTaskAwaiter.m_task
屬性,這也是一個Task
類型的屬性
如果是IStateMachineBoxAwareAwaiter
類型的話,調用IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted()
方法,傳遞的是當前的IAsyncStateMachineBox
狀態機盒子實例,具體實現咱們待會看
如果上面的條件都不滿足的話,則調用ICriticalNotifyCompletion.UnsafeOnCompleted()
方法,傳遞的是IAsyncStateMachineBox.MoveNextAction
方法,IAsyncStateMachineBox
實現類包裝了IAsyncStateMachine
實現類,這里的stateMachineBox.MoveNextAction
本質是在執行IAsyncStateMachine的MoveNext
的方法,即我們狀態機里我們自己寫的業務邏輯。
我們首先來看一下StateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted()
方法,找到一個實現類。因為它的實現類有好幾個,比如ConfiguredValueTaskAwaiter
、ValueTaskAwaiter
、YieldAwaitable
等,這里咱們選擇有類型的ConfiguredValueTaskAwaiter
實現類,看一下AwaitUnsafeOnCompleted
方法
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) { object? obj = _value._obj; Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource); if (obj is Task t) { //如果是Task類型的話會調用TaskAwaiter.UnsafeOnCompletedInternal方法,也是上面咱們多次提到的 TaskAwaiter.UnsafeOnCompletedInternal(t, box, _value._continueOnCapturedContext); } else if (obj != null) { Unsafe.As<IValueTaskSource>(obj).OnCompleted(ThreadPool.s_invokeAsyncStateMachineBox, box, _value._token, _value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else { //兜底的方法也是TaskAwaiter.UnsafeOnCompletedInternal TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value._continueOnCapturedContext); } }
可以看到ConfiguredValueTaskAwaiter.AwaitUnsafeOnCompleted()
方法最終也是執行到了TaskAwaiter.UnsafeOnCompletedInternal()
方法,這個咱們上面已經多次提到了。接下里咱們再來看一下ICriticalNotifyCompletion.UnsafeOnCompleted()
方法里的實現是啥,咱們找到它的一個常用的實現類,也是咱們上面狀態機幫咱們生成的TaskAwaiter<>
類里的實現
public void UnsafeOnCompleted(Action continuation) { TaskAwaiter.OnCompletedInternal(m_task, continuation, true, false); } //TaskAwaiter的OnCompletedInternal方法 internal static void OnCompletedInternal(Task task, Action continuation, bool continueOnCapturedContext, bool flowExecutionContext) { ArgumentNullException.ThrowIfNull(continuation, "continuation"); if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled) { continuation = OutputWaitEtwEvents(task, continuation); } //這里調用了Task的SetContinuationForAwait方法 task.SetContinuationForAwait(continuation, continueOnCapturedContext, flowExecutionContext); }
咱們看到了這里調用的是Task的SetContinuationForAwait
方法,上面我們提到的AwaitUnsafeOnCompleted
方法里直接調用了TaskAwaiter
的UnsafeOnCompletedInternal
方法,咱們可以來看一下里面的實現
internal static void UnsafeOnCompletedInternal(Task task, IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext) { if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled) { //默認情況下我們是沒有去監聽EventSource發布的時間消息 //如果你開啟了EventSource日志的監聽則會走到這里 task.SetContinuationForAwait(OutputWaitEtwEvents(task, stateMachineBox.MoveNextAction), continueOnCapturedContext, false); } else { task.UnsafeSetContinuationForAwait(stateMachineBox, continueOnCapturedContext); } }
因為默認是沒有開啟EventSource
的監聽,所以上面的兩個TplEventSource.Log.IsEnabled
相關的邏輯執行不到,如果代碼里堅挺了相關的EventSource
則會執行這段邏輯。SetContinuationForAwait
方法和UnsafeSetContinuationForAwait
方法邏輯是一致的,只是因為如果開啟了EventSource
的監聽會發布事件消息,其中包裝了關于異步信息的事件相關。所以我們可以直接來看UnsafeSetContinuationForAwait
方法實現
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext) { if (continueOnCapturedContext) { //winform wpf等ui線程包含同步上下文SynchronizationContext相關的信息 //如果存在則直接在SynchronizationContext同步上線文中的Post方法把異步結果在ui線程中完成回調執行 SynchronizationContext current = SynchronizationContext.Current; if (current != null && current.GetType() != typeof(SynchronizationContext)) { SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = new SynchronizationContextAwaitTaskContinuation(current, stateMachineBox.MoveNextAction, false); if (!AddTaskContinuation(synchronizationContextAwaitTaskContinuation, false)) { synchronizationContextAwaitTaskContinuation.Run(this, false); } return; } //判斷是否包含內部任務調度器,如果不是默認的TaskScheduler.Default調度策略,也就是ThreadPoolTaskScheduler的方式執行MoveNext //則使用TaskSchedulerAwaitTaskContinuation的Run方法執行MoveNext TaskScheduler internalCurrent = TaskScheduler.InternalCurrent; if (internalCurrent != null && internalCurrent != TaskScheduler.Default) { TaskSchedulerAwaitTaskContinuation taskSchedulerAwaitTaskContinuation = new TaskSchedulerAwaitTaskContinuation(internalCurrent, stateMachineBox.MoveNextAction, false); if (!AddTaskContinuation(taskSchedulerAwaitTaskContinuation, false)) { taskSchedulerAwaitTaskContinuation.Run(this, false); } return; } } //執行兜底邏輯使用線程池執行 if (!AddTaskContinuation(stateMachineBox, false)) { ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, true); } }
上面我們提到過IAsyncStateMachineBox
實現類包裝了IAsyncStateMachine
實現類,它的stateMachineBox.MoveNextAction
本質是在執行AsyncStateMachine的MoveNext
的方法,即我們狀態機里的自己的業務邏輯。根據上面的邏輯我們來大致總結一下相關的執行策略
如果包含SynchronizationContext
同步上下文,也就是winform wpf等ui線程,則直接在SynchronizationContext同步上線文中的Post方法把異步結果在ui線程中完成回調執行,里面的核心方法咱們待會會看到
如果TaskScheduler
調度器不是默認的ThreadPoolTaskScheduler
調度器,則使用自定義的TaskScheduler來執行MoveNext方法,統一里面的核心方法咱們待來看
兜底的邏輯則是使用線程池來執行,即使用ThreadPool的UnsafeQueueUserWorkItemInternal
方法
好了上面留下了兩個核心的方法,沒有展示相關的實現,首先咱們來看下TaskSchedulerAwaitTaskContinuation的Run
方法,這個方法適用于存在同步上下文的場景,來看下它的核心邏輯
internal sealed override void Run(Task task, bool canInlineContinuationTask) { //判斷當前線程同步上下文是否和傳遞的同步上下文一致,則直接執行,說明當前線程可以直接使用異步結果 if (canInlineContinuationTask && m_syncContext == SynchronizationContext.Current) { RunCallback(AwaitTaskContinuation.GetInvokeActionCallback(), m_action, ref Task.t_currentTask); return; } //如果不是同一個同步上下文則執行PostAction委托 RunCallback(PostAction, this, ref Task.t_currentTask); } private static void PostAction(object state) { //通過傳遞的state來捕獲執行回調的同步上下文,這里使用的SynchronizationContext的非阻塞的Post方法來執行后續邏輯 SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = (SynchronizationContextAwaitTaskContinuation)state; synchronizationContextAwaitTaskContinuation.m_syncContext.Post(s_postCallback, synchronizationContextAwaitTaskContinuation.m_action); } protected void RunCallback(ContextCallback callback, object state, ref Task currentTask) { //捕獲執行上下文,異步執行完成之后在執行上下文中執行后續邏輯 ExecutionContext capturedContext = m_capturedContext; if (capturedContext == null) { //核心邏輯就是再行上面的委托即AwaitTaskContinuation.GetInvokeActionCallback方法或PostAction方法 callback(state); } else { ExecutionContext.RunInternal(capturedContext, callback, state); } }
上面的方法省略了一些邏輯,為了讓邏輯看起來更清晰,我們可以看到里面的邏輯,即在同步上下文SynchronizationContext
中執行異步的回調的結果。如果當前線程就包含同步上下文則直接執行,如果不是則使用之前傳遞進來的同步上下文來執行。執行的時候會嘗試捕獲執行上下文。咱們還說到了如果TaskScheduler
調度器不是默認的ThreadPoolTaskScheduler
調度器,則使用自定義的TaskScheduler來執行MoveNext方法,來看下里面的核心實現
internal sealed override void Run(Task ignored, bool canInlineContinuationTask) { //如果當前的scheduler策略是TaskScheduler.Default即默認的ThreadPoolTaskScheduler //則直接使用默認策略調度任務 if (m_scheduler == TaskScheduler.Default) { base.Run(ignored, canInlineContinuationTask); return; } //如果不是默認策略則使用,我們定義的TaskScheduler Task task = CreateTask(delegate(object state) { try { ((Action)state)(); } catch (Exception exception) { Task.ThrowAsync(exception, null); } }, m_action, m_scheduler);//這里的m_scheduler指的是自定義的TaskScheduler bool flag = canInlineContinuationTask && (TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread); //或者是task其他形式的策略執行 if (flag) { TaskContinuation.InlineIfPossibleOrElseQueue(task, false); return; } try { task.ScheduleAndStart(false); } catch (TaskSchedulerException) { } }
這個邏輯看起來比較清晰,即根據Task的執行策略TaskScheduler
判斷如何執行任務,比如默認的ThreadPoolTaskScheduler
策略,或其他策略,比如單線程策略或者自定義的等等。
上面的執行過程可以總結為以下兩點
是否是Task
調度,否則執行默認的ThreadPool.UnsafeQueueUserWorkItemInternal()
執行。如果是TaskScheduler
則判斷是哪一種策略,比如是默認的ThreadPoolTaskScheduler
或是其它策略亦或是自定義策略等。
是否包含同步上下文SynchronizationContext
,比如UI線程,大家都知道修改界面控件需要在UI線程上才能執行,但是await
操作可能存在線程切換如果await的結果需要在UI展示需要同步上下文保證異步的結果在UI線程中執行。
如果任務需要執行中,我們總得想辦法把結果給相應的Task
實例,這樣我們才能在執行完成之后把得到對應的執行狀態或者執行結果在相關的Task中體現出來,方便我們判斷Task是否執行完成或者獲取相關的執行結果,在ThreadPoolWorkQueue
中有相關的邏輯具體在DispatchWorkItem
方法中
private static void DispatchWorkItem(object workItem, Thread currentThread) { //判斷在線程池中自行的任務書否是Task任務 Task task = workItem as Task; if (task != null) { task.ExecuteFromThreadPool(currentThread); } else { Unsafe.As<IThreadPoolWorkItem>(workItem).Execute(); } }
ThreadPool
里的線程執行了Task的ExecuteWithThreadLocal
的方法,核心執行方法在Task的ExecuteWithThreadLocal
,這樣的話執行相關的結果就可以體現在Task
實例中,比如Task的IsCompleted
屬性判斷是否執行完成,或者Task<TResult>的GetResult
f方法獲取結果等等。
這里需要注意的是Task.FromResult<TResult>(TResult)
這個方法,相信大家經常用到,如果你的執行結果需要包裝成Task<TResult>
總會用到這個方法。它的意思是創建一個Task<TResult>,并以指定結果成功完成。
,也就是Task<TResult>的IsCompleted屬性為true
,這個結論可以在dotnet api
中Task.FromResult(TResult)文檔中看到,因為我們只需要把我們已有的結果包裝成Task所以不涉及到復雜的執行,這也意味著在生成狀態機的時候MoveNext
方法里的邏輯判斷IsCompleted
時候代表任務是直接完成的,會直接通過GetResult()
獲取到結果,不需要AwaitUnsafeOnCompleted
去根據執行策略執行
private void MoveNext() { int num = <>1__state; try { TaskAwaiter<string> awaiter; if (num != 0) { awaiter = Task.FromResult("Hello World").GetAwaiter(); //這里的IsCompleted會為true不會執行相關的執行策略 if (!awaiter.IsCompleted) { <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } } else { } <>s__2 = awaiter.GetResult(); } catch (Exception exception) { } <>t__builder.SetResult(); }
“怎么使用c#異步操作async await狀態機”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。