async await 死锁简单分析
虽然写了很多async和await代码,有时候由于一些原因,还是发生了死锁,所以这次就简单的分析下。
死锁代码
private void Button_Click(object sender, EventArgs e) { var jsonTask = GetJsonAsync(new Uri("https://www.toolhelper.cn/admin/SystemManage/DataDict/GetDataDictListJson")); string result = jsonTask.Result.ToString(); // 步骤1.主线程在这里被 jsonTask.Result 阻塞,直到 jsonTask 完成 } public static async Task<JObject> GetJsonAsync(Uri uri) { // 步骤2.当前所在的线程是主线程 using (var client = new HttpClient()) { var jsonString = await client.GetStringAsync(uri); // 步骤3.GetStringAsync 执行完成后,返回步骤2所在的线程(主线程),但是主线程在步骤1已经被阻塞 return JObject.Parse(jsonString); } }
改进一,加上 ConfigureAwait(false)
private void Button_Click(object sender, EventArgs e) { var jsonTask = GetJsonAsync(new Uri("https://www.toolhelper.cn/admin/SystemManage/DataDict/GetDataDictListJson")); string result = jsonTask.Result.ToString(); // 步骤1.主线程在这里被 jsonTask.Result 阻塞,直到 jsonTask 完成 } public static async Task<JObject> GetJsonAsync(Uri uri) { // 步骤2.当前所在的线程是主线程 using (var client = new HttpClient()) { var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false); // 步骤3.GetStringAsync 执行完成后,根据ConfigureAwait(false),此行后面的代码不返回到步骤2所在的线程(主线程) return JObject.Parse(jsonString); } }
改进二,使用标准写法
private async void Button_Click(object sender, EventArgs e) { var jsonTask = await GetJsonAsync(new Uri("https://www.toolhelper.cn/admin/SystemManage/DataDict/GetDataDictListJson")); // 步骤1.这种写法,主线程不会被阻塞 string result = jsonTask.ToString(); } public static async Task<JObject> GetJsonAsync(Uri uri) { // 步骤2.当前所在的线程是主线程或者是其他线程 using (var client = new HttpClient()) { var jsonString = await client.GetStringAsync(uri); // 步骤3.GetStringAsync 执行完成后,后面的代码返回到步骤2所在的线程 return JObject.Parse(jsonString); } }
如果处理任务需要在界面上显示加载中(spinning)图片,使用async仍然会阻塞主线程,一种解决的办法如下。
private async Task LoadData() { await Task.Run(() => { //Work done here }); // Update UI if necessary. }
如果本文的描述的方法或内容有问题,请给我留言。