網站首頁 編程語言 正文
??上一篇末尾提到了Awaiter這個類型,上一篇說了,能await的對象,必須包含GetAwaiter()方法,不清楚的朋友可以看上篇文章。那么,Awaiter到底有什么特別之處呢?
??首先,從上篇文章我們知道,一個Awaiter必須實現INotifyCompletion接口,這個接口定義如下:
namespace System.Runtime.CompilerServices { ////// Represents an operation that will schedule continuations when the operation completes. /// public interface INotifyCompletion { ///Schedules the continuation action to be invoked when the instance completes. /// The action to invoke when the operation completes. ///The void OnCompleted(Action continuation); } }argument is null (Nothing in Visual Basic).
??除此之外還必須包含IsCompleted屬性和包含GetResult()方法。
??注意OnCompleted的參數是一個Action委托,并且不出意外的話,委托里面總會有一個地方調用一個MoveNext()方法,它推動狀態機到達下一個狀態,然后執行下一個狀態需要執行的代碼。
??那么,知道這個有什么用呢?第一,它是你充分了解async/await這套機制的基礎,包括與之相關的同步上下文、執行上下文、死鎖問題等,第二,它可以實現一些特殊的功能。
??從上一篇我們知道,OnCompleted中的contination的主要目的是推動狀態機的執行,也就是推動異步方法中await后面部分的代碼執行。從這里看出,continuation的執行是受我們控制的,因此我們可以直接執行它,或是等待某個條件成熟然后執行它,我們可以把它放到線程池執行,也可以單獨起一個線程執行。譬如,我們可以讓await后面部分的代碼直接在線程池上執行。
public static async Task AwaiterTest() { Console.WriteLine($"是否是線程池線程?{Thread.CurrentThread.IsThreadPoolThread}"); await default (SkipToThreadPoolAwaiter); Console.WriteLine($"是否是線程池線程?{Thread.CurrentThread.IsThreadPoolThread}"); } static void Main(string[] args) { _ = AwaiterTest(); Console.ReadLine(); } public struct SkipToThreadPoolAwaiter : INotifyCompletion { public bool IsCompleted => false; public void GetResult() { Console.WriteLine("調用GetResult以獲取結果"); } public void OnCompleted(Action continuation) { Console.WriteLine("調用OnCompleted,把Await后面部分要執行的代碼傳遞過來(傳遞MoveNext,以推動狀態機流轉)"); ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("開始執行Await后面部分的代碼"); continuation(); Console.WriteLine("后面部分的代碼執行完畢"); }); Console.WriteLine("返回調用線程"); } public SkipToThreadPoolAwaiter GetAwaiter() { Console.WriteLine("獲得Awaiter"); return this; } }
??這是一個控制臺程序,輸出結果如下。
是否是線程池線程?False
獲得Awaiter
調用OnCompleted,把Await后面部分要執行的代碼傳遞過來(傳遞MoveNext,以推動狀態機流轉)
返回調用線程
開始執行Await后面部分的代碼
調用GetResult以獲取結果
是否是線程池線程?True
后面部分的代碼執行完畢
??特別注意一下,第五步說明可能有點疑惑,怎么第六步不是打印是否是線程池線程?原因是部分awaiter是有返回值的,在執行await后面部分的代碼時,會首先調用GetResult()以獲取結果。這對編譯器改造異步方法來說是一個固定的模式(上篇文章沒有體現這一步)。
??把Awaiter改成有返回值嘗試。
public static async Task AwaiterTest() { Console.WriteLine($"是否是線程池線程?{Thread.CurrentThread.IsThreadPoolThread}"); var res = await default (SkipToThreadPoolAwaiter); Console.WriteLine($"結果是{res}"); } static void Main(string[] args) _ = AwaiterTest(); Console.ReadLine(); public struct SkipToThreadPoolAwaiter : INotifyCompletion public bool IsCompleted => false; public int GetResult() { Console.WriteLine("調用GetResult以獲取結果"); return 1; } public void OnCompleted(Action continuation) Console.WriteLine("調用OnCompleted,把Await后面部分要執行的代碼傳遞過來(傳遞MoveNext,以推動狀態機流轉)"); ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("開始執行Await后面部分的代碼"); continuation(); Console.WriteLine("后面部分的代碼執行完畢"); }); Console.WriteLine("返回調用線程"); public SkipToThreadPoolAwaiter GetAwaiter() Console.WriteLine("獲得Awaiter"); return this;
??輸出如下
是否是線程池線程?False
獲得Awaiter
調用OnCompleted,把Await后面部分要執行的代碼傳遞過來(傳遞MoveNext,以推動狀態機流轉)
返回調用線程
開始執行Await后面部分的代碼
調用GetResult以獲取結果
結果是1
是否是線程池線程?True
后面部分的代碼執行完畢
??對照前面的文章來看,相信你應該有所得,能解決你部分的疑惑。前面說到,我們可以控制continuation的執行,那如果當前線程有同步上下文(SychronizationContext),我們是不是可以放到同步上下文中執行?TaskAwaiter是會這么做的,如果你不想它使用同步上下文,你可以在Task實例上調用ConfigureAwait(false),它表面后面部分的代碼將不會使用同步上下文執行。
??另外說一下Task.Yield()這個Awaiter,他的行為是捕捉同步上下文,如果有,則會放到同步上下文中執行,如果沒有,則會放到線程池中執行。在窗體程序中,有時候你打開一個模態對話框,會導致主窗體部分的動畫沒有反應,在模態對話框關閉之后,才會反應。原因是模態對話框阻塞了主窗體的消息循環,也就是阻塞了主線程,如果想讓動畫先完成,然后再打開模態對話框,則可以在打開模態對話框之前,Await Task.Yield(),這也對應了它的意思,讓渡之意。
??后面文章還會說明同步上下文具體是什么、異步代碼中使用同步代碼會導致死鎖的本質原因、如何實現類似Task的類,并且怎么與Async/await這套機制搭配使用等知識。
??覺得有收獲的不妨點個贊,有支持才有動力寫出更好的文章。
原文鏈接:https://www.cnblogs.com/hkfyf/p/15949528.html
相關推薦
- 2022-09-19 正則表達式regexp_replace的使用方法_正則表達式
- 2022-03-25 Redis分布式鎖如何實現續期_Redis
- 2022-10-16 Ant?Design?組件庫之步驟條實現_React
- 2022-07-13 卸載Kafka導致的broker id前后不一致,Zookeeper側清理殘留數據指導
- 2024-02-25 前端顯示的日期時間與數據庫日期時間不一致
- 2022-05-06 react-router-domV6版本的路由和嵌套路由寫法詳解_React
- 2022-11-19 Linux下定時自動備份Docker中所有SqlServer數據庫的腳本_docker
- 2023-06-18 C#中獲取文件大小問題_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支