C#異步編程async/await用法詳解
異步函數(shù)簡介
一般指 async 修飾符聲明得、可包含await表達(dá)式得方法或匿名函數(shù)。
聲明方式
異步方法的聲明語法與其他方法完全一樣, 只是需要包含 async 關(guān)鍵字。async可以出現(xiàn)在返回值之前的任何位置, 如下示例:
async public static void GetInfoAsync()
{
//...
}
public async static void GetInfoAsync()
{
//...
}
public static async void GetInfoAsync()
{
//...
}異步方法的返回類型
異步函數(shù)的返回類型只能為: void、Task、Task<TResult>。
Task<TResult>: 代表一個返回值T類型的操作。
Task: 代表一個無返回值的操作。
void: 為了和傳統(tǒng)的事件處理程序兼容而設(shè)計。
await(等待)
await等待的是什么? 可以是一個異步操作(Task)、亦或者是具備返回值的異步操作(Task<TResult>)的值, 如下:
public async static void GetInfoAsync()
{
await GetData(); // 等待異步操作, 無返回值
await GetData<int>(1); //等待異步操作, 返回值 int
}
static Task GetData()
{
//...
return null;
}
static Task<T> GetData<T>(int a)
{
//...
return null;
}注: await 最終操作的是一個值, 當(dāng)然, 也可以是無值, 如上GetData() , 否則就是一個 Task<T> 如上: GetData<T>()
await執(zhí)行過程

TaskAwaiter 獲取執(zhí)行結(jié)果
一般而言, await等待的一個異步操作, 無論是具備返回值還是否, 那么最終都會獲得該操作是否已完成、具備返回值得異步操作可以獲取他得返回結(jié)果。
所以這個時候,TaskAwaiter出現(xiàn)了, 無論是Task、還是Task<TResult>操作, 都具備GetAwaiter() 方法。
用于獲取改操作得狀態(tài)、返回結(jié)果, 及部分操作, 如下TaskAwaiter結(jié)構(gòu):
//
// 摘要:
// 提供等待異步任務(wù)完成的對象。
public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
//
// 摘要:
// 獲取一個值,該值指示是否已完成的異步任務(wù)。
//
// 返回結(jié)果:
// true 如果任務(wù)已完成;否則為 false。
//
// 異常:
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。
public bool IsCompleted { get; }
//
// 摘要:
// 結(jié)束異步任務(wù)完成之前的等待。
//
// 異常:
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。
//
// T:System.Threading.Tasks.TaskCanceledException:
// 任務(wù)已取消。
//
// T:System.Exception:
// 在完成的任務(wù) System.Threading.Tasks.TaskStatus.Faulted 狀態(tài)。
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public void GetResult();
//
// 摘要:
// 設(shè)置時應(yīng)執(zhí)行的操作 System.Runtime.CompilerServices.TaskAwaiter 對象停止等待異步任務(wù)完成。
//
// 參數(shù):
// continuation:
// 要在等待操作完成時執(zhí)行的操作。
//
// 異常:
// T:System.ArgumentNullException:
// continuation 為 null。
//
// T:System.NullReferenceException:
// System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。
[SecuritySafeCritical]
public void OnCompleted(Action continuation);
//
// 摘要:
// 計劃程序與此等待異步任務(wù)的延續(xù)任務(wù)操作。
//
// 參數(shù):
// continuation:
// 要等待操作完成時調(diào)用的操作。
//
// 異常:
// T:System.ArgumentNullException:
// continuation 為 null。
//
// T:System.InvalidOperationException:
// 該等待程序未正確初始化。
[SecurityCritical]
public void UnsafeOnCompleted(Action continuation);
}接下來, 演示如何通過等待去獲取異步操作的返回結(jié)果, 如下代碼所示:
public async static void GetInfoAsync()
{
Task<bool> task = Task.Run<bool>(() =>
{
Thread.Sleep(10000); //模擬耗時
return true;
});
//以下兩種方式
bool taskResult1 = await task; //內(nèi)部自己執(zhí)行了GetAwaiter()
bool taskResult = task.GetAwaiter().GetResult(); //自己手動執(zhí)行Awaiter(), 但是阻塞UI
Console.WriteLine(taskResult);
}注: 對于一個await表達(dá)式, 編譯器生成的代碼會先調(diào)用GetAwaiter(), 然后通過awaiter得成員來等待結(jié)果, 所以以上兩種方式等效( 不考慮阻塞的情況下)
為了驗證以上猜測, 通過反編譯工具查看得到如下代碼:

編譯器最終生成兩個密封類, 一個類( <>c )我們展開分析:
<GetInfoAsync>b__1_0() 正是模擬耗時的一個操作委托生成的方法。
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9 = new Program.<>c();
public static Func<bool> <>9__1_0;
internal bool <GetInfoAsync>b__1_0()
{
Thread.Sleep(10000);
return true;
}
}第二個類<GetInfoAsync>d__1 分析:
該類分別實現(xiàn)了接口 IAsyncStateMachine 的MoveNext() 與 SetStateMachine() ,另外 注意,
還特別定義了一個 <>t__builder, 先記住他, 下面講會對他講到, 為什么編譯器生成的代碼會默認(rèn)先調(diào)用GetAwaiter()
[CompilerGenerated]
private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine
{
public int <>1__state;
public AsyncVoidMethodBuilder <>t__builder;
private Task<bool> <task>5__1;
private bool <result>5__2;
private bool <>s__3;
private TaskAwaiter<bool> <>u__1;
void IAsyncStateMachine.MoveNext()
{
int num = this.<>1__state;
try
{
TaskAwaiter<bool> awaiter;
if (num != 0)
{
Func<bool> arg_2F_0;
if ((arg_2F_0 = Program.<>c.<>9__1_0) == null)
{
arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>9.<GetInfoAsync>b__1_0));
}
this.<task>5__1 = Task.Run<bool>(arg_2F_0);
awaiter = this.<task>5__1.GetAwaiter();
if (!awaiter.IsCompleted)
{
this.<>1__state = 0;
this.<>u__1 = awaiter;
Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this;
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__);
return;
}
}
else
{
awaiter = this.<>u__1;
this.<>u__1 = default(TaskAwaiter<bool>);
this.<>1__state = -1;
}
this.<>s__3 = awaiter.GetResult();
this.<result>5__2 = this.<>s__3;
Console.WriteLine(this.<result>5__2);
}
catch (Exception exception)
{
this.<>1__state = -2;
this.<>t__builder.SetException(exception);
return;
}
this.<>1__state = -2;
this.<>t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}接下來, 看GetInfoAsync()方法, 這個是自己編寫的, 但是實現(xiàn)的細(xì)節(jié),最終轉(zhuǎn)換成了編譯器執(zhí)行代碼:
[AsyncStateMachine(typeof(Program.<GetInfoAsync>d__1)), DebuggerStepThrough]
public static void GetInfoAsync()
{
Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = new Program.<GetInfoAsync>d__1();
<GetInfoAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
<GetInfoAsync>d__.<>1__state = -1;
AsyncVoidMethodBuilder <>t__builder = <GetInfoAsync>d__.<>t__builder;
<>t__builder.Start<Program.<GetInfoAsync>d__1>(ref <GetInfoAsync>d__); //注意到該代碼, 調(diào)用了Start(),也許這就是默認(rèn)實現(xiàn)的地方
}通過查看Start泛型方法的實現(xiàn), 最終找到了, 該泛型的條件限制于必須實現(xiàn)與IAsyncStateMachine 接口, 所以通過查看, 該類最終調(diào)用了 MoveNext(), 而MoveNext中正
調(diào)用了GetAwaiter()。關(guān)于Start的實現(xiàn)如下所示:
[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
throw new ArgumentNullException("stateMachine");
}
ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);
RuntimeHelpers.PrepareConstrainedRegions();
try
{
ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);
stateMachine.MoveNext();
}
finally
{
executionContextSwitcher.Undo();
}
}剖析MoveNext

對比IDE中的代碼, 如下所示:

總結(jié)
await等待的是任務(wù)的操作值, 最終返回是異步操作的返回結(jié)果。而這一切都是因為編譯器創(chuàng)建了一系列復(fù)雜的狀態(tài)機制, 以達(dá)到其實現(xiàn)。
到此這篇關(guān)于C#異步編程async/await用法詳解的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#實現(xiàn)的優(yōu)酷真實視頻地址解析功能(2014新算法)
這篇文章主要介紹了C#實現(xiàn)的優(yōu)酷真實視頻地址解析功能(2014新算法),本文在當(dāng)前環(huán)境下是有效的,因為優(yōu)酷之前更新了算法,需要的朋友可以參考下2014-10-10
C#連接操作 MySQL 數(shù)據(jù)庫實例(使用官方驅(qū)動)
這篇文章主要介紹了C#連接操作 MySQL 數(shù)據(jù)庫實例(使用官方驅(qū)動),本文講解了C#中的Mysql連接方法和SQL操作方法,需要的朋友可以參考下2015-02-02
C#程序中使用LINQ to XML來查詢XML格式數(shù)據(jù)的實例
這篇文章主要介紹了C#程序中使用LINQ to XML來查詢XML格式數(shù)據(jù)的實例,LINQ to XML是.NET框架中集成的接口,可以將XML數(shù)據(jù)放到內(nèi)存中進行處理,需要的朋友可以參考下2016-03-03
c#中使用BackgroundWorker的實現(xiàn)
本文主要介紹了c#中使用BackgroundWorker的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
C#實現(xiàn)子窗體與父窗體通信方法實例總結(jié)
這篇文章主要介紹了C#實現(xiàn)子窗體與父窗體通信方法,實例總結(jié)了常用的四種窗體通信方法,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09

