網(wǎng)站首頁 編程語言 正文
異步函數(shù)簡(jiǎn)介
一般指 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>: 代表一個(gè)返回值T類型的操作。
Task: 代表一個(gè)無返回值的操作。
void: 為了和傳統(tǒng)的事件處理程序兼容而設(shè)計(jì)。
await(等待)
await等待的是什么? 可以是一個(gè)異步操作(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 最終操作的是一個(gè)值, 當(dāng)然, 也可以是無值, 如上GetData() , 否則就是一個(gè) Task<T> 如上: GetData<T>()
await執(zhí)行過程
TaskAwaiter 獲取執(zhí)行結(jié)果
一般而言, await等待的一個(gè)異步操作, 無論是具備返回值還是否, 那么最終都會(huì)獲得該操作是否已完成、具備返回值得異步操作可以獲取他得返回結(jié)果。
所以這個(gè)時(shí)候,TaskAwaiter出現(xiàn)了, 無論是Task、還是Task<TResult>操作, 都具備GetAwaiter() 方法。
用于獲取改操作得狀態(tài)、返回結(jié)果, 及部分操作, 如下TaskAwaiter結(jié)構(gòu):
// // 摘要: // 提供等待異步任務(wù)完成的對(duì)象。 public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion { // // 摘要: // 獲取一個(gè)值,該值指示是否已完成的異步任務(wù)。 // // 返回結(jié)果: // true 如果任務(wù)已完成;否則為 false。 // // 異常: // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對(duì)象未正確初始化。 public bool IsCompleted { get; } // // 摘要: // 結(jié)束異步任務(wù)完成之前的等待。 // // 異常: // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對(duì)象未正確初始化。 // // 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è)置時(shí)應(yīng)執(zhí)行的操作 System.Runtime.CompilerServices.TaskAwaiter 對(duì)象停止等待異步任務(wù)完成。 // // 參數(shù): // continuation: // 要在等待操作完成時(shí)執(zhí)行的操作。 // // 異常: // T:System.ArgumentNullException: // continuation 為 null。 // // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對(duì)象未正確初始化。 [SecuritySafeCritical] public void OnCompleted(Action continuation); // // 摘要: // 計(jì)劃程序與此等待異步任務(wù)的延續(xù)任務(wù)操作。 // // 參數(shù): // continuation: // 要等待操作完成時(shí)調(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); //模擬耗時(shí) return true; }); //以下兩種方式 bool taskResult1 = await task; //內(nèi)部自己執(zhí)行了GetAwaiter() bool taskResult = task.GetAwaiter().GetResult(); //自己手動(dòng)執(zhí)行Awaiter(), 但是阻塞UI Console.WriteLine(taskResult); }
注: 對(duì)于一個(gè)await表達(dá)式, 編譯器生成的代碼會(huì)先調(diào)用GetAwaiter(), 然后通過awaiter得成員來等待結(jié)果, 所以以上兩種方式等效( 不考慮阻塞的情況下)
為了驗(yàn)證以上猜測(cè), 通過反編譯工具查看得到如下代碼:
編譯器最終生成兩個(gè)密封類, 一個(gè)類( <>c )我們展開分析:
<GetInfoAsync>b__1_0() 正是模擬耗時(shí)的一個(gè)操作委托生成的方法。
[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; } }
第二個(gè)類<GetInfoAsync>d__1 分析:
該類分別實(shí)現(xiàn)了接口 IAsyncStateMachine 的MoveNext() 與 SetStateMachine() ,另外 注意,
還特別定義了一個(gè) <>t__builder, 先記住他, 下面講會(huì)對(duì)他講到, 為什么編譯器生成的代碼會(huì)默認(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()方法, 這個(gè)是自己編寫的, 但是實(shí)現(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)實(shí)現(xiàn)的地方 }
通過查看Start泛型方法的實(shí)現(xiàn), 最終找到了, 該泛型的條件限制于必須實(shí)現(xiàn)與IAsyncStateMachine 接口, 所以通過查看, 該類最終調(diào)用了 MoveNext(), 而MoveNext中正
調(diào)用了GetAwaiter()。關(guān)于Start的實(shí)現(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
對(duì)比IDE中的代碼, 如下所示:
總結(jié)
await等待的是任務(wù)的操作值, 最終返回是異步操作的返回結(jié)果。而這一切都是因?yàn)榫幾g器創(chuàng)建了一系列復(fù)雜的狀態(tài)機(jī)制, 以達(dá)到其實(shí)現(xiàn)。
原文鏈接:https://www.cnblogs.com/zhaoshujie/p/11192036.html
相關(guān)推薦
- 2022-07-18 win10無法訪問ubuntu共享文件夾(smbd出錯(cuò)排查)
- 2022-05-13 python魔法方法之__setattr__()_python
- 2022-03-30 android?RecyclerView添加footerview詳解_Android
- 2023-07-18 優(yōu)化MybatisPlus的saveBatch方法
- 2022-04-19 c#中token的使用方法實(shí)例_C#教程
- 2022-11-15 python重用父類功能的兩種方式實(shí)例詳解_python
- 2022-09-06 C語言常見排序算法之交換排序(冒泡排序,快速排序)_C 語言
- 2022-03-29 一篇文章帶你理解React?Props的?原理_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支