前面已經講解了task的運行、阻塞、同步、延續操作、取消等!今天我們就專門來聊聊關於async/await的那一些事,分析其實現原理,通過該文章你也該對async的使用還有更加清晰的理解 ...
hello task,咱們又見面啦!!是不是覺得很熟讀的開場白,哈哈你喲這感覺那就對了,說明你已經閱讀過了我總結的前面4篇關於task的文章,謝謝支持!感覺不熟悉的也沒有關係,在文章末尾我會列出前四篇文章的地址,可以點擊詳細閱讀。
前幾篇文章分享了以後,無論是公眾號還是博客園,都有小伙伴問我async/await的專欄總結分享,既然這樣,那今天我們就專門來聊聊關於async/await的那一些事,通過該文章你也該對async的使用還有更加清晰的理解,謝謝!
async/await入門:
async也就是我們說的非同步方法,不廢話,也不先說那麼多的大理論,先上一個簡單的實例,通過這個簡單的實例實現和asyncd 初相識!!
static void Main(string[] args) { Console.WriteLine($"主線程開始,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); // 同步實現 AddSync(1, 2); // 非同步方法,沒有 Await AddNoAwaitSyncHas(1, 2); // 非同步方法,有 Await AddHasAwaitAsync(1, 2); Console.WriteLine($"主線程結束,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); Console.ReadLine(); return; } /// <summary> /// 同步計算兩個數字之和 /// </summary> /// <param name="num1">參數1</param> /// <param name="num2">參數2</param> /// <returns></returns> private static int AddSync(int num1, int num2) { Thread.Sleep(1000); Console.WriteLine($"同步方法,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); return num1 + num2; } /// <summary> /// 對兩個數字求和 (非同步方法,沒有 Await) /// </summary> /// <param name="num1">參數1</param> /// <param name="num2">參數2</param> /// <returns>結果</returns> private static async Task<int> AddNoAwaitSyncHas(int num1, int num2) { Console.WriteLine($"非同步線程沒有await前,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); // 兩個數字求和,假設其中會涉及到很耗時的邏輯 Thread.Sleep(1000); Console.WriteLine($"非同步線程沒有await後,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); return num1 + num2; } /// <summary> /// 對兩個數字求和 (非同步方法,有 Await) /// </summary> /// <param name="num1">參數1</param> /// <param name="num2">參數2</param> /// <returns>結果</returns> private static async Task<int> AddHasAwaitAsync(int num1, int num2) { Console.WriteLine($"非同步線程await前,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); // 兩個數字求和,假設其中會涉及到很耗時的邏輯 var add = Add(num1, num2); int result = await add; Console.WriteLine($"非同步線程await後,線程ID:{Thread.CurrentThread.ManagedThreadId}\n"); return result; } /// <summary> /// Task 對兩個數字求和 /// </summary> /// <param name="num1">參數1</param> /// <param name="num2">參數2</param> /// <returns>結果</returns> private static Task<int> Add(int num1, int num2) { // 假設該邏輯執行起來很耗時 var task = Task.Run(() => { Console.WriteLine($"我是Task內部執行開始:線程ID :{Thread.CurrentThread.ManagedThreadId}\n"); Thread.Sleep(5000); Console.WriteLine($"我是Task內部執行結束:線程ID :{Thread.CurrentThread.ManagedThreadId}\n"); return num1 + num2; }); return task; }
執行結果:
結合代碼和執行結果,我們分析可以得出以下一些結論:
1、通過async的寫法和同步方法在實現和調用上都很相似
2、非同步方法async如果沒有await關鍵詞,其整體執行都是在主線中運行
----同步調用
3、非同步方法async有await關鍵詞,其線程執行分水嶺就在await
----await前,async執行還是在主線中執行
----await後,async的執行邏輯會新開一個線程
----也就是說,async其真正的非同步還是await實現
----而await修飾的實際是一個task修飾的變數或者返回的類型為task的方法體
----所以最後的最後,async的非同步還是通過task來實現的
4、await是不能單獨使用,一定是在是和async成對使用
----當然aysnc修飾的方法可以沒有await關鍵詞
通過上面的一個簡單實例,是不是發現要實現一個非同步方法,是不是so easy,是的 ,你沒說錯,就是那麼簡單,但是也許你會問,幹嘛實現一個非同步方法整的的如此複雜,創建了這麼多方法,是的,不急不急,我這樣寫,是為了更加清晰的明白其執行流程。好了,下麵我們在一起來探討一下aysnc/await的組成結構吧!
aysnc/await的組成結構:
其實非同步方法的整體結構和一個普通的方法沒有多大區別,唯一不一樣的點,就是多了一個task邏輯主體,下麵簡單的分別來概要說明一下每一個環節:
上面的圖簡單的繪製了一個非同步方法在整體執行時的一個執行順序。
非同步方法調用
個人覺得這個沒有什麼說的,其實很普通方法調用一樣,只是說非同步方法的調用結果一般為一個Task對象,那麼需要獲獲取其執行結果的值,或者對執行結果需要做一些邏輯處理,這個和操作一個普通的task一樣,這兒就不在細說,不清楚的可以看我前面分享的幾篇文章,會有詳細的說明,謝謝!
aysnc的方法體
通過實例我們應該已經知道,其實非同步方法,也就是在普通的方法體上,加了一個async修飾罷了,其簡單的結構大概是
private aysnc task MyAysnc(){具體方法實現}
說說aysnc的返回類型
其返回類型有三種情況,每一種情況適用於不同的業務場景,如下:
A、Tsak:其主要適用場景是,主程式只關心非同步方法執行狀態,不需要和主線程有任何執行結果數據交互。
B、Task<T>:其主要適用場景是,主程式不僅僅關心非同步方法執行狀態,並且還希望執行後返回一個數據類型為T的結果
C、void: 主程式既不關係非同步方法執行狀態,也不關心其執行結果,只是主程式調用一次非同步方法,對於除事件處理程式以外的代碼,通常不鼓勵使用 async void 方法,因為調用方不能
task邏輯主體
aysnc為了實現非同步,其中最關鍵的一個點就是await修飾符,await修飾的也就是task實現邏輯主體。task實現邏輯主體,其實在上就是一個task實例,所以其裡面的實例邏輯使用和一個普通的task實例定義操作都是一樣的,在此也就不在詳細說明,前面的幾篇文章也有詳細的說明瞭,如果不清楚的可以查看以前的幾篇文章。
aysnc/await的原理分析:
在說這一塊之前,我們先把寫的代碼編譯後,在通過反編譯後發現在代碼裡面根本找不到aysnc/await關鍵詞,有興趣的小伙伴,你也可以這樣操作分析一下。那麼我們就明白了aysnc/await其實是編譯器層面給的一個語法糖,是為了方便實現一個非同步方罷了。
從反編譯後的代碼看出編譯器新生成一個繼承IAsyncStateMachine 的狀態機結構asyncd(代碼中叫<AddHasAwaitAsync>d__2),下麵是基於反編譯後的代碼來分析的。
IAsyncStateMachine最基本的狀態機介面定義:
public interface IAsyncStateMachine { void MoveNext(); void SetStateMachine(IAsyncStateMachine stateMachine); }
好了,說道這兒我們已經知道aysnc/await是編程器層面的一個語法糖,那麼我們在來分析一下其執行的流程如下:
第一步:主線程調用 AddHasAwaitAsync(1,2)非同步方法
第二步:AddHasAwaitAsync()方法內初始化狀態機狀態為-1,啟動<AddHasAwaitAsync>d__2
第三步:MoveNext方法內部開始執行,task.run實現了把業務邏輯執行丟到線程池中,返回一個可等待的任務句柄。其底層還是藉助委托實現。
第四步:到此程式以及開啟了兩個線程,一個主線程,一個task線程,兩個線程相互獨立互不阻塞,各自執行對應的業務邏輯。
好了,時間不早了,就先到這兒吧,感覺這一篇文章總結的不怎麼好,先這樣,後續我們在持續交流,謝謝!
猜您喜歡:
第一篇:聊聊多線程哪一些事兒(task)之 一創建運行與阻塞
第三篇:聊聊多線程那一些事兒(task)之 三 非同步取消和非同步方法
第四篇:聊聊多線程那一些事兒 之 四 經典應用(取與舍、動態創建)
END
為了更高的交流,歡迎大家關註我的公眾號,掃描下麵二維碼即可關註,謝謝: