避免上下文延續 在預設情況下,一個 async 方法在被 await 調用後恢復運行時,會在原來的上下文中運行。 為了避免在上下文中恢復運行,可讓 await 調用 ConfigureAwait 方法的返回值,參數 設為 false : 可能導致性能問題 作者Stephen提到,當在UI線程大量使用 ...
避免上下文延續
在預設情況下,一個 async 方法在被 await 調用後恢復運行時,會在原來的上下文中運行。
為了避免在上下文中恢復運行,可讓 await 調用 ConfigureAwait 方法的返回值,參數 continueOnCapturedContext
設為 false :
async Task ResumeOnContextAsync () {
await Task.Delay (TimeSpan.FromSeconds (1));
// 這個方法在同一個上下文中恢復運行。
}
async Task ResumeWithoutContextAsync () {
await Task.Delay (TimeSpan.FromSeconds (1)).ConfigureAwait (false);
// 這個方法在恢復運行時,會丟棄上下文。
}
可能導致性能問題
作者Stephen提到,當在UI線程大量使用async方法,可能需要考慮線程切換導致上下文恢復導致的性能消耗。當然性能消耗問題不會是單一的原因導致,代碼的優化永無止境。
可能會有人對性能消耗的理解不太具體,大概解釋一下,async方法會生成一個狀態機,該狀態機可能是一個class或者struct用來存儲上下文信息,這些都要消耗存儲空間和進行後續的GC回收。
關於狀態機的更多信息可以訪問《C#併發編程經典實例》學習筆記—非同步編程關鍵字 Async和Await 查看。
Stephen提到UI線程中如果每秒有1000個任務就太多了。這個結論來自於視頻Tip 6: Async library methods should consider using Task.ConfigureAwait(false)
避免使用不當導致死鎖
文章Talk: Async best practices給出的第6條建議提到作為一個類庫提供者,應該需要註意ConfigureAwait的問題,避免該類庫的使用者在UI線程使用該類庫時產生額外的性能消耗。還提到類庫使用者對非同步方法不當的使用時將會導致死鎖,而避免該類死鎖的最佳辦法是,參數
continueOnCapturedContext 設為 false 即使用ConfigureAwait (false)
。
個人總結一下不當的使用包括但不限於以下幾類:
- 同步方法中使用非同步方法。所以一個類庫的提供者,應儘量提供一個方法的同步實現和非同步實現,併在非同步實現中使用
ConfigureAwait (false)
。 - 在UI線程使用
Task.Wait()
或者Task.Result
。
參考文章:
- https://blog.walterlv.com/post/deadlock-in-task-wait.html
- https://blog.walterlv.com/post/using-configure-await-to-avoid-deadlocks.html
- https://blogs.msdn.microsoft.com/lucian/2013/02/17/talk-the-new-async-design-patterns/