C# 異步編程中 await
實現(xiàn)原理詳解
在C#中,async
和 await
關鍵字用于編寫異步代碼。本文將詳細介紹 await
的實現(xiàn)原理,包括狀態(tài)機的生成、回調(diào)函數(shù)的注冊和觸發(fā)等關鍵步驟。
1. 異步方法的基本概念
在C#中,async
關鍵字標記一個方法為異步方法,而 await
關鍵字用于等待一個異步操作完成。異步方法可以提高程序的響應性和性能,特別是在處理I/O操作和網(wǎng)絡請求時。
2. 示例異步方法
我們以一個簡單的異步方法為例,來詳細解釋 await
的實現(xiàn)原理。
public class Example
{
public async Task<int> CalculateAsync()
{
int a = await Task.Run(() => 10);
int b = await Task.Run(() => 20);
return a + b;
}
}
3. 編譯器生成的狀態(tài)機
編譯器會為每個異步方法生成一個狀態(tài)機。狀態(tài)機是一個結構體,包含了異步方法的所有局部變量和狀態(tài)信息。
編譯器生成的狀態(tài)機類
public class Example
{
public Task<int> CalculateAsync()
{
<CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
stateMachine.<>4__this = this;
stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[AsyncMethodBuilder(typeof(AsyncTaskMethodBuilder<int>))]
private struct <CalculateAsync>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<int> <>t__builder;
public Example <>4__this;
public int <a>5__1;
public TaskAwaiter<int> <>u__1;
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<int> awaiter;
switch (num)
{
case 0:
goto TR_0000;
case 1:
<>1__state = -1;
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<int>);
goto TR_0001;
case 2:
<>1__state = -1;
break;
default:
<>1__state = 0;
awaiter = Task.Run<int>(() => 10).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
goto TR_0000;
}
TR_0000:
<a>5__1 = awaiter.GetResult();
awaiter = Task.Run<int>(() => 20).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 1);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
TR_0001:
int b = awaiter.GetResult();
int result = <a>5__1 + b;
<>1__state = -2;
<>t__builder.SetResult(result);
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
}
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
}
4. 實現(xiàn)流程詳解
初始化狀態(tài)機
在 CalculateAsync
方法中,創(chuàng)建狀態(tài)機實例 <CalculateAsync>d__0
。
<CalculateAsync>d__0 stateMachine = new <CalculateAsync>d__0();
stateMachine.<>4__this = this;
stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
stateMachine.<>1__state = -1;
<>4__this
:指向當前實例,即 Example
類的實例。<>t__builder
:創(chuàng)建 AsyncTaskMethodBuilder<int>
實例,用于管理任務的生命周期。<>1__state
:初始化狀態(tài)為 -1
,表示方法尚未開始執(zhí)行。
開始執(zhí)行
調(diào)用 Start
方法開始執(zhí)行異步方法。Start
方法會調(diào)用狀態(tài)機的 MoveNext
方法。
stateMachine.<>t__builder.Start(ref stateMachine);
執(zhí)行方法體
在 MoveNext
方法中,根據(jù)當前狀態(tài) <>1__state
執(zhí)行相應的代碼。
private void MoveNext()
{
int num = <>1__state;
try
{
TaskAwaiter<int> awaiter;
switch (num)
{
}
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
}
}
遇到 await
遇到第一個 await
關鍵字時,調(diào)用 Task.Run(() => 10).GetAwaiter()
獲取 Awaiter
對象。
awaiter = Task.Run<int>(() => 10).GetAwaiter();
- 檢查
awaiter.IsCompleted
,如果任務已經(jīng)完成,直接調(diào)用 awaiter.GetResult()
獲取結果。 - 如果任務未完成,記錄當前狀態(tài)
<>1__state
,保存 awaiter
對象,并調(diào)用 <>t__builder.AwaitUnsafeOnCompleted
注冊回調(diào)。
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
注冊回調(diào)
AwaitUnsafeOnCompleted
方法會注冊一個回調(diào),當任務完成時,回調(diào)會被觸發(fā)。
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
}
awaiter.UnsafeOnCompleted
方法注冊一個回調(diào)函數(shù),該回調(diào)函數(shù)會在任務完成時被觸發(fā)。stateMachine.MoveNext
是一個委托,指向狀態(tài)機的 MoveNext
方法。
任務完成
當任務完成時,回調(diào)會被觸發(fā),重新調(diào)用 MoveNext
方法,恢復異步方法的執(zhí)行。
public void OnCompleted(Action continuation)
{
task.ContinueWith(_ => continuation(), TaskScheduler.Default);
}
繼續(xù)執(zhí)行
從上次暫停的地方繼續(xù)執(zhí)行方法體。
TR_0000:
<a>5__1 = awaiter.GetResult();
awaiter = Task.Run<int>(() => 20).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 1);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
方法完成
當所有異步操作完成并計算出結果后,設置狀態(tài) <>1__state
為 -2
,表示方法已經(jīng)完成。
int b = awaiter.GetResult();
int result = <a>5__1 + b;
<>1__state = -2;
<>t__builder.SetResult(result);
- 調(diào)用
<>t__builder.SetResult
設置任務的結果。 - 如果在執(zhí)行過程中拋出異常,捕獲異常并調(diào)用
<>t__builder.SetException
設置任務的異常。
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
}
5. 深入理解 AsyncTaskMethodBuilder
AsyncTaskMethodBuilder
是一個輔助類,用于構建和管理異步方法的任務。它提供了以下方法:
Create
:創(chuàng)建一個新的 AsyncTaskMethodBuilder
實例。Start
:開始執(zhí)行異步方法,調(diào)用狀態(tài)機的 MoveNext
方法。AwaitUnsafeOnCompleted
:注冊回調(diào)函數(shù),當任務完成時觸發(fā)回調(diào)。SetResult
:設置任務的結果。SetException
:設置任務的異常。
AsyncTaskMethodBuilder
的內(nèi)部實現(xiàn)
AsyncTaskMethodBuilder
內(nèi)部維護了一個 Task
對象,用于表示異步操作的結果。當異步方法完成時,SetResult
方法會設置任務的結果,SetException
方法會設置任務的異常。
public struct AsyncTaskMethodBuilder<TResult>
{
private Task<TResult> task;
public static AsyncTaskMethodBuilder<TResult> Create()
{
return new AsyncTaskMethodBuilder<TResult>(new Task<TResult>());
}
private AsyncTaskMethodBuilder(Task<TResult> task)
{
this.task = task;
}
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
awaiter.OnCompleted(stateMachine.MoveNext);
}
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
}
public void SetResult(TResult result)
{
task.SetResult(result);
}
public void SetException(Exception exception)
{
task.SetException(exception);
}
public Task<TResult> Task => task;
}
6. 異步方法的生命周期
異步方法的生命周期可以分為以下幾個階段:
- 初始化:創(chuàng)建狀態(tài)機實例,初始化狀態(tài)和任務構建器。
- 開始執(zhí)行:調(diào)用
Start
方法開始執(zhí)行異步方法。 - 執(zhí)行方法體:在
MoveNext
方法中,根據(jù)當前狀態(tài)執(zhí)行相應的代碼。 - 遇到
await
:檢查任務是否完成,如果未完成則注冊回調(diào)并暫停方法執(zhí)行。 - 任務完成:回調(diào)被觸發(fā),重新調(diào)用
MoveNext
方法,恢復異步方法的執(zhí)行。 - 方法完成:所有異步操作完成,設置任務的結果或異常。
7. 異步方法的優(yōu)勢
使用 async
和 await
編寫的異步方法有以下優(yōu)勢:
- 提高響應性:異步方法不會阻塞主線程,應用程序可以繼續(xù)響應用戶的輸入和其他事件。
- 提高性能:異步方法可以并發(fā)執(zhí)行多個任務,充分利用系統(tǒng)資源。
- 簡化代碼:異步方法的代碼結構類似于同步方法,易于理解和維護。
8. 異步方法的注意事項
盡管 async
和 await
提供了許多優(yōu)勢,但在使用時也需要注意以下幾點:
- 避免
async void
:async void
方法主要用于事件處理程序,其他情況下應避免使用,因為它無法被等待,并且異常處理較為困難。 - 異常處理:異步方法中的異常會被包裝在
AggregateException
中,需要特殊處理。 - 資源管理:異步方法中使用
using
語句時,需要注意 Dispose
方法的調(diào)用時機。
9. 完整的流程圖
為了更好地理解這個過程,可以用流程圖來展示:
總結
通過上述詳細的解釋和示例代碼,我們可以總結出以下幾點:
- 異步方法的基本概念:
async
和 await
關鍵字用于編寫異步代碼。 - 狀態(tài)機的生成:編譯器為每個異步方法生成一個狀態(tài)機,包含所有局部變量和狀態(tài)信息。
MoveNext
方法的執(zhí)行:MoveNext
方法是狀態(tài)機的核心,負責管理和執(zhí)行異步操作。- 回調(diào)函數(shù)的注冊和觸發(fā):
- 當遇到
await
關鍵字時,編譯器會生成代碼來檢查任務是否已經(jīng)完成。 - 如果任務未完成,注冊回調(diào)并暫停方法執(zhí)行。
- 當任務完成時,回調(diào)函數(shù)會被觸發(fā),重新調(diào)用狀態(tài)機的
MoveNext
方法,從而恢復異步方法的執(zhí)行。
AwaitUnsafeOnCompleted
方法的作用:在任務完成時注冊一個回調(diào)函數(shù),回調(diào)函數(shù)會在任務完成后被觸發(fā),從而恢復異步方法的執(zhí)行。
轉自?https://www.cnblogs.com/Bob-luo/p/18518463
該文章在 2024/11/4 10:26:31 編輯過