微軟官方的MSDN上說async和await是“異步”,但是不少人(包括筆者自己)都有一些誤區需要澄清:為什么await語句之后沒有執行?不是異步嗎?
先舉一個示例代碼如下:
public partial class Form1 : Form{ public async Task Processing() { await Task.Delay(5000); label1.Text = "Succuessful"; } public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { await Processing(); MessageBox.Show("Button's event completed"); }}
很多人(包括筆者)一開始會覺得異步好像類似多線程一樣,到await的時候會在后臺先開啟一個線程執行任務,隨后主線程(這里是UI線程)將自動執行后面的部分(即彈出“Button's event completed”的消息框)。
其實這個理解是錯誤的。async和await的本質其實是“yield return”和“LINQ”的“迭代式”等待。我們應該清楚一點:那就是你寫了LINQ語句:
var results = from …… select ……;foreach(var r in results){ ……}
當你下斷點你會發覺results并不會立即執行,直到使用到results的地方(例子中也就是foreach這里)才會被執行(此時黃色跟蹤調試的光棒又會折回到var results……這里,然后等到results執行完畢之后才真正進入foreach進行執行)。
所以,async/await和LINQ的這種“迭代式”的“異步操作”是異曲同工的。只不過async/await本質是返回一個Task而已,而Task又是異步的(因為Task本質就是一個線程),所以真正執行到(使用到async方法的時候)帶有await的方法的時候,后臺才會真正開啟一個線程去執行任務。此時主線程會等待這個Task線程直到其執行完畢(IsComplete屬性為True為止)。所以界面是不會卡頓的。
所以,await是Task的異步等待而已,并不是我們所謂的“異步操作”;拿它和LINQ作對比,你會發現LINQ執行順序和它一致,只不過LINQ沒有異步等待(當然沒有!又沒有開啟線程啥的……)。
我們進一步可以這樣對比:
LINQ:變量 = LINQ語句(表達式)
等到使用LINQ變量的時候才折返到LINQ語句處真正執行LINQ語句。
異步等待:變量 = 異步方法
等到使用await+異步方法的時候才會折返到該異步方法處,開啟線程真正執行異步方法,主線程被掛起(但不會造成界面死掉),直至子線程Task任務完全執行完畢為止。
在LINQ中,你如果需要立即執行,可以使用擴展方法:
var results = (from ……
select ……).ToList();
因為立即使用到了這個LINQ語句,所以會被立即執行。
同樣地,異步等待也可以變成類似Wait一樣的同步等待:
private async void button1_Click(object sender, EventArgs e){ Processing().GetAwaiter().GetResult(); MessageBox.Show("Button's event completed");}
因為Processing本來就返回Task,當然也可以使用Wait進行同步等待。
新聞熱點
疑難解答