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