隨意使用異步的await和Result,被弄得欲仙欲死,而後看了 Don't Block on Async Code,稍許明白,翻譯而後加上本身的理解以加深印象。html
會死鎖的兩個例子json
UI例子異步
public static async Task<JObject> GetJsonAsync(Uri uri) { using (var client = new HttpClient()) { var jsonString = await client.GetStringAsync(uri); return JObject.Parse(jsonString); } } // My "top-level" method. public void Button1_Click(...) { var jsonTask = GetJsonAsync(...); textBox1.Text = jsonTask.Result; }
** ASP.NET例子**async
public static async Task<JObject> GetJsonAsync(Uri uri) { 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(); } }
死鎖的緣由單元測試
await 一個Task後,當Task完成後將繼續一個Context。測試
UI例子的content是 UI content,ASP.NET例子的Content是request content。在任什麼時候候,這兩個content只能屬於一個線程,是不能被具體的線程捆綁(tied)。這個有趣或者噁心的特點沒被官方文檔說明,只在my MSDN article about SynchronizationContext。線程
上面兩個例子的運行過程是:翻譯
防止死鎖code
兩點經驗:htm
根據第一點經驗:
var jsonString = await client.GetStringAsync(uri);
改爲
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
根據第二點經驗,調用異步方法的代碼以下:
public async void Button1_Click(...) { var json = await GetJsonAsync(...); textBox1.Text = json; } public class MyController : ApiController { public async Task<string> Get() { var json = await GetJsonAsync(...); return json.ToString(); } }
await 是一個異步等待
.Result是一個同步等待
同步等待在控制檯程序、單元測試中不會死鎖