天天看點

《C#并發程式設計經典執行個體》學習筆記—2.7 避免上下文延續

避免上下文延續

在預設情況下,一個 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/