徹底搞懂C#非同步程式設計 async和await的原理
1.前提
熟練掌握Task並行程式設計。
2.用Task並行解釋async和await非同步
因為控制檯有多執行緒操作的優化,因此這裡選擇winform來做示例。
測試程式碼如下所示:
有三個textbox,一個button
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace TestAsyncAwait { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Button1_Click(object sender, EventArgs e) { TestAsync(); textBox3.Text = "333"; } private async void TestAsync() { //Thread.Sleep(5000);//依然阻塞 await Task.Run(() => { Thread.Sleep(2000); this.Invoke((EventHandler)delegate { textBox1.Text = "1"; }); Thread.Sleep(2000); }); this.Invoke((EventHandler)delegate { textBox2.Text = "22"; }); } } }
顯示的順序是:333,1,22
如果在設定textbox顯示內容之前,通過Thread.CurrentThread.ManagedThreadId屬性來獲取當前執行緒ID。
可以得到textbox3所在為主執行緒,await之前也在主執行緒,await中和await後為新執行緒,這也是為什麼在textbox1和textbox2的text加上invoke的原因。
簡單來時,實際上async和await就是表示,遇到await之後,函式直接返回,然後剩下的部分等同於在一個新執行緒中執行。
使用Task並行程式設計的解釋程式碼如下,將button按鈕的click事件與TestAsync函式統一成TestTask函式
private void TestTask() { Task.Run(() => { Task task = Task.Run(() => { Thread.Sleep(2000); this.Invoke((EventHandler)delegate { textBox1.Text = "1"; }); Thread.Sleep(2000); }); Task.WaitAll(task); this.Invoke((EventHandler)delegate { textBox2.Text = "22"; }); }); textBox3.Text = "333"; }
當然,上述程式碼也不完全等價async和await,只不過在實現功能上來說,沒有差別。
具體的差別表現為,waitall之前的執行緒與waitall之後所線上程不在同一個執行緒。這個差別也揭示了async和await的另外一個優勢,那就是節約執行緒,內部實現了執行緒池優化。
3.總結
如果你懂得Task並行程式設計或者Thread多執行緒程式設計,其實async和await的原理是不難的,沒有什麼神乎其神的操作。
async和await作為C#非同步程式設計的語法糖,具有無可匹敵的優勢,但是很多時候很多人不能理解用法,相比較而言直接使用Task並行程式設計會更加易懂。
我的建議是,如果是新專案可以使用async和await,讓自己的程式碼更簡潔;如果是老專案維護,建議還是使用Task的方式維護,容易讀懂。