close

前一篇有講到async的概念

這篇來看看使用async要注意的一件事情,就是可能會造成deadlock的情況

 

這邊要先有一個小觀念,就是context

context中文翻譯叫做前後文,在這邊就是指"當下的執行環境和狀態"

在async和await的機制下,遇到await的時候,會有一個專門的物件來紀錄context,這個物件叫做SynchronizationContext

為什麼需要這個物件呢,主要的目的是為了簡化用切換執行緒的操作

比如說有個操作,從UI點個按鍵 >> 拿資料 >>更新UI

其中點按鍵和更新UI都是在UI的thread完成,拿資料是在另一個thread,所以中間會需要做thread的切換

如果是使用await的話,系統就會幫忙handle這些事情,依照內部的演算法來看是要切換執行緒去執行

或是說是使用SynchronizationContext.current去紀錄當前的context,以便task完成後可以在這個context底下繼續下去

 

而造成deadlock的原因,就是因為這個SynchronizationContext,以下是個範例:

這個是從 https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html 貼來的

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  // (real-world code shouldn't use HttpClient in a using block; this is just example code)
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}

這邊可以先注意到一件事情,就是Get()這個方法並不是async方法

所以在執行到GetJsonAsync(...);這一行的時候,會把這個thread block在這邊等待這個task完成

同時也把SynchronizationContext也鎖在這邊,因為他在等待GetJsonAsync(...)裡面回傳的那個Task完成,才能繼續接下來的動作

而在進到GetJsonAsync(...);裡面呢,因為裡面有個await,在client.GetStringAsync(uri);完成之後,會去找SynchronizationContext來完成接下來的動作

只是這時候最外層的thread已經SynchronizationContext給block住了(因為thread在等待task完成),所以就造成兩邊都在等的情況

Task在等SynchronizationContext,thread佔住SynchronizationContext在等Task

這篇裡面有比較詳細的順序說明:

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

https://www.huanlintalk.com/2016/01/asyc-deadlock-in-aspbet.html

 

不過總歸一句就是,因為有thread把它block住了,才會造成這種情況

所以第一個解決方式就是把所有方法都改成async方法

第二種呢就是在async方法的await後面加上ConfigureAwait(false)

這樣當await方法執行完之後,如果拿不到SynchronizationContext的話,他會另外用一個thread去完成剩下的工作

 

話是這麼說,其實我對這些觀念也不是特別清楚,似懂非懂這樣

不過個人是覺得寫多就會慢慢掌握訣竅了

就... 好好努力吧XD

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 頁頁頁六滴 的頭像
    頁頁頁六滴

    人森很精彩,所以要把所有事情都記起來=ˇ=

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()