net非同步編程
㈠ 如何正確理解.NET 4.5和C#5.0中的async/await非同步編程模式
這個await,其實只是把對老版本C#迭代器的慣用法官方化了。現在很多平台都因為一些原因不得不用舊版本的C#,比如unity,想非同步那隻能通過迭代器來做。
async、迭代器都是語法糖,編譯器會幫你實現成一個狀態機匿名類,實例裡面hold住一些臨時變數,記錄一下當前狀態。根據你寫的yield/await,把一個非同步方法拆成幾個同步block,根據一定規則定期的去MoveNext一下,Current是Task那我就根據你配置的線程上下文決定把這個Task跑在哪個線程上。
那麼用await修飾的非同步方法是在哪個線程中被調用的?為什麼上面這個事件處理方法不會阻塞GUI?
我還看到其它一些描述是說使用async/await非同步模式不會生成新的線程,那麼只在原來已有線程的基礎上面如何做到非同步運行?
題主這個例子這個方法就是在UI線程調用的,並且沒有ConfigureAwait(false),所以會在當前await時捕捉的UI線程上下文執行之後的同步block。
至於為什麼不會阻塞,可以簡單理解為執行了第一個block,碰到Delay(4000),給UI線程的定時器掛一個4000時間之後再調用下一個同步塊的回調。
看題主說的書名像是國產的書,這方面還是看《CLR via C#》或者《Concurrency in C# cookbook》比較好。
㈡ 什麼是非同步編程
傳統的同步編程是一種請求響應模型,調用一個方法,等待其響應返回.
非同步編程就是要重新考慮是否需要響應的問題,也就是縮小需要響應的地方。因為越快獲得響應,就是越同步化,順序化,事務化,性能差化。
非同步編程通常是通過fire and forget方式實現,發射事件後即忘記,做別的事情了,無需立即等待剛才發射的響應結果了。(發射事件的地方稱為生產者,而將在另外一個地方響應事件的處理者稱為消費者).非同步編程是一種事件驅動編程,需要完全改變思路,將「請求響應」的思路轉變到「事件驅動」思路上,是一種軟體編程思維的轉變.下面幾種你看參考一下
1、非同步編程模型 (APM) 模式(也稱為 IAsyncResult 模式),其中非同步操作要求 Begin 和 End 方法(例如,非同步寫操作的 BeginWrite 和 EndWrite)。對於新的開發工作不再建議採用此模式。
2、基於事件的非同步模式 (EAP) 需要一個具有 Async 後綴的方法,還需要一個或多個事件、事件處理程序、委託類型和 EventArg 派生的類型。EAP 是在 .NET Framework 2.0 版中引入的。對於新的開發工作不再建議採用此模式。
3、基於任務的非同步模式 (TAP),該模式使用一個方法表示非同步操作的啟動和完成。.NET Framework 4 中引入了 TAP,並且是 .NET Framework 中非同步編程的建議方法。
㈢ C#幾種非同步編程
1、非同步編程模型 (APM) 模式(也稱為 IAsyncResult 模式),其中非同步操作要求 Begin 和 End 方法(例如,非同步寫操作的 BeginWrite 和 EndWrite)。對於新的開發工作不再建議採用此模式。
2、基於事件的非同步模式 (EAP) 需要一個具有 Async 後綴的方法,還需要一個或多個事件、事件處理程序、委託類型和 EventArg 派生的類型。EAP 是在 .NET Framework 2.0 版中引入的。對於新的開發工作不再建議採用此模式。
3、基於任務的非同步模式 (TAP),該模式使用一個方法表示非同步操作的啟動和完成。.NET Framework 4 中引入了 TAP,並且是 .NET Framework 中非同步編程的建議方
㈣ 如何正確理解.NET 4.5和C#5.0中的async/await非同步編程模式
相對於之前Begin/End模式和事件模式,async/await模式讓程序員得以用同步的代碼結構進行非同步編程。async/await入門很方便,但是深入理解卻涉及很多領域,如線程池、同步上下文等等。我斷斷續續接觸了幾個月,稍微有一些心得:
await的作用是等待非同步Task完成,並不是阻塞的。舉個例子,一個非同步方法:
publicasyncTaskCaller()
{
Action0();
awaitMethod();
Action3();
}
publicasyncTaskMethod()
{
Action1();
awaitTask.Delay(1000);
Action2();
}
A.當你在非UI線程A上執行Caller(),將完成以下幾件事:
[線程A]執行Action0()
[線程A]調用await Method()
[線程A]執行Action1()
[線程A]啟動任務Task.Delay(1000),並在線程池裡安插一個新任務,在Task.Delay(1000)完成後,由另一個線程執行6
[線程A]去處理別的事情
[線程B]執行Action2()
[線程B]await Method()返回
[線程B]執行Action3()
其中,線程A和線程B並不保證是同一個線程。如果你在await前後列印線程ID,你會發現ID是不同的。
B.當你在UI線程上執行Caller(),過程有了變化:
[UI線程]執行Action0()
[UI線程]調用await Method()
[UI線程]執行Action1()
[UI線程]啟動任務Task.Delay(1000),並在線程池裡安插一個新任務,在Task.Delay(1000)完成後,由另一個線程執行6
[UI線程]去處理別的事情
[線程C]在UI線程的同步上下文中執行7(類似於在窗體類上執行Invoke()方法)
[UI線程]執行Action2()
[UI線程]await Method()返回
[UI線程]執行Action3()
可見,當使用await方法的線程為UI線程時,程序默認會通過第6步,保證await前後是同一個線程ID。這個當然是有一定性能犧牲的(甚至會造成死鎖,在D里會討論),如果你不想在await完成後回到UI線程,見C。
C. 你可以在UI線程上使用await XXX().ConfigureAwait(false)去替代awaitXXX(),來禁止當await XXX()結束時恢復線程。舉個例子,執行下列代碼是沒問題的(如B里描述的):
privateasyncvoidbutton1_Click(objectsender,EventArgse)
{
this.Text="123";
awaitTask.Delay(1000);
this.Text="321";
}
但是,執行下列代碼就會發生「線程間操作無效」的錯誤:
privateasyncvoidbutton1_Click(objectsender,EventArgse)
{
this.Text="123";
awaitTask.Delay(1000).ConfitureAwait(false);
this.Text="321";//線程間操作無效
}
因為執行
this.Text="321";
的線程已經不再是UI線程。
D. 順便一提,Task.Wait()方法,相比於await Task,會同步地執行Task。但是,如果你在UI線程上Wait的Task里本身又有await,那麼將會產生死鎖:
privatevoidFoo(objectsender,EventArgse)
{
this.Text="123";
Method().Wait();//此處發生死鎖
this.Text="321";//這行永遠也不會執行
}
privateasyncTaskMethod()
{
awaitTask.Delay(1000);
}
為什麼呢?Method().Wait()會阻塞UI線程等待Method()完成,但是參照B過程,在await完成後,Method()完成前,是需要恢復到UI線程的,但是此時UI線程已經被阻塞了,因此死鎖就發生了。
要避免這個死鎖,可以參照C。
E. 說出來你可能不信,上面的都是我手打的。在內容上雖然不一定嚴謹,但希望對樓主和其它新接觸TAP的朋友有一定啟發。