private void Form2_Load(object sender, EventArgs e) { Console.WriteLine("main:"+Thread.CurrentThread.ManagedThreadId); DoSth(); //不加await不等待DoSth執行完成直接先彈出form2_load MessageBox.Show("form2_load"); } async Task DoSth() { Do1(); //不加await不等待Do1執行完畢(先彈出 DoSth executed after 3s 後彈出 do1 after 5s)至關於併發,加了await就會等待Do1執行完再執行後續至關於阻塞 await Task.Delay(3000); //這裏不能用Thread.Sleep代替 緣由見結尾處紅色處文字(若是不開線程則Thread.Sleep會阻塞住主線程 看不到先彈出 DoSth executed after 3s效果) Console.WriteLine("dosth:"+Thread.CurrentThread.ManagedThreadId); MessageBox.Show("DoSth executed after 3s"); } async Task Do1() { await Task.Delay(5000);//這裏不能用Thread.Sleep代替 緣由見結尾處紅色處文字(若是不開線程則Thread.Sleep會阻塞住主線程 看不到先彈出 DoSth executed after 3s效果) Console.WriteLine("do1:"+Thread.CurrentThread.ManagedThreadId); MessageBox.Show("do1 after 5s"); }
線程id輸出3個都同樣 緣由(http://www.cnblogs.com/mushroom/p/4575417.html):html
有言論說Async不用開線程,也有說須要開線程的,從單一方面來說都是對的,也都是錯的。 上面源碼是從簡分析的,具體async內部會涉及到線程上下文切換,線程複用、調度等。 想深刻的同窗能夠研究下ExecutionContextSwitcher、 SecurityContext.RestoreCurrentWI、ExecutionContext這幾個東東。編程
其實具體的物理線程細節能夠不用太關心,知道其【主線程A邏輯->異步任務線程B邏輯->主線程C邏輯】這個基本原理便可。 另外Async也會有線程開銷的,因此要合理分業務場景去使用。多線程
From:http://www.cnblogs.com/xuejianxiyang/p/7089280.html併發
https://www.zhihu.com/question/30601778異步
最近在看《C#圖解教程》,其中第20章講解了.NET 4.5和C# 5.0新添加的異步編程模型async/await,舉了一個在GUI中執行異步操做的例子:async
private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
{
btnDoStuff.IsEnabled = false;
lblStatus.Content = "Doing Stuff";
await Task.Delay(4000);
lblStatus.Content = "Not Doing Anything";
btnDoStuff.IsEnabled = true;
}
上面這個方法是一個按鈕空間Click事件的事件響應代碼。
個人問題是:按照個人理解,控件的響應代碼應該是在GUI線程裏面被調用的,並且對於GUI應用程序來講,GUI線程通常只有一個,而且全部和GUI控件方面的交互都應該經過GUI線程來完成。那麼用await修飾的異步方法是在哪一個線程中被調用的?爲何上面這個事件處理方法不會阻塞GUI?
我還看到其它一些描述是說使用async/await異步模式不會生成新的線程,那麼只在原來已有線程的基礎上面如何作到異步運行?
總之,如何正確理解.NET 4.5和C# 5.0中的async/await異步編程模式?異步編程
await修飾的方法返回的是一個Task,而這個Task其實就是一個異步句柄,若是我來取名字的話多半就叫作IAsyncHandler。函數
一個IAsyncHandler你能夠想象成是這麼一個東西:public interface IAsyncHandler
{
Register( Action continuation );
}
這是僞代碼,事實上不存在這麼個東西。spa
private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
{
btnDoStuff.IsEnabled = false;
lblStatus.Content = "Doing Stuff";
var handler = Task.Delay(4000) as IAsyncHandler
handler.Register( () =>
{
lblStatus.Content = "Not Doing Anything";
btnDoStuff.IsEnabled = true;
} );
}
固然上面全是僞代碼,可是若是你能看懂這段代碼在幹什麼,那麼async基本就能夠懂了,剩下的只是一些實現細節上的問題。線程
一般狀況下,Task.Delay會當即返回一個Task對象,這個Task對象會在指定時間以後被標記爲Completed,而被標記Completed就會當即開一個線程來進行延續的操做。
可是這裏有個問題就是你這個方法是寫在UI線程裏面的,控件的事件會被UI線程觸發,而UI線程上有個SynchronizationContext對象,這個對象的存在就會使得系統在異步回調的時候去捕獲源線程。在原來的線程(UI線程)去執行延續的任務。
而咱們知道WinForm裏面有個方法叫作Control.Invoke,能夠把一個方法封送到UI線程去執行,而上面的工做和這個方法底層的原理實際上是同樣的,因此,其實這段代碼用傳統的思惟來理解的話像是這樣:private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
{
btnDoStuff.IsEnabled = false;
lblStatus.Content = "Doing Stuff";
Action continuation = () =>
{
lblStatus.Content = "Not Doing Anything";
btnDoStuff.IsEnabled = true;
};
Thread.Start( () =>
{
Thread.Sleep( 4000 );
Control.Invoke( continuation );
} );
}
異步並不是必然是多線程的,譬如 yield 。
我舉個例子,我顯示一串文字,等用戶按一下按鈕再顯示下一串,在第一次顯示文字跟用戶按下按鈕之間,能夠作不少事情,這裏就是異步的,但不是多線程的。
有沒有辦法把兩次顯示字符串的操做,寫在同一個函數裏?能夠,最簡單的方法就是用yield。按鈕按下去時候就調用IEnumerator.MoveNext()
await 自己也不會創造線程,致使出現線程的是後面的那個執行Task 的函數。Task 概念上也不是多線程的,它只是把任務放到了線程池,可是若是任務還沒執行,立刻需求結果,它就會在當前線程執行,而非在另外一個線程來執行。
因此異步和多線程之間,概念是要分清楚的。
另外,在F#裏面,會有更多的異步模型,只是C#裏暫時只能使用Task這個自動擋,讓人模糊了概念,只差了一個轉身。
https://www.cnblogs.com/renjing/p/Invoke.html