C# ConfigureAwait(false)在UI程序中避免了死锁
ConfigureAwait(false):当 await 等待完成时,它会尝试在线程池上下文中执行 await 之后的代码
ConfigureAwait(true):当 async 方法内的 await 执行完成时,它会尝试获取之前调用者线程所在的上下文执行方法的剩余部分
在Winform程序中没有使用ConfigureAwait(false)导致的死锁示例:
新建一个winform桌面程序,页面如下
在button1的单击处理事件代码如下:
1 private void button1_Click(object sender, EventArgs e) 2 { 3 MessageBox.Show($"开始运行,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "button1_Click"); 4 Test(); 5 MessageBox.Show($"运行结束,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "button1_Click"); 6 } 7 8 public static void Test() 9 { 10 var task = XXXAsync(); // 开启异步任务,比如下载某个资源 11 var resuslt = task.Result; // 获取异步任务的结果显示给用户,这里会阻塞当前ui线程直到异步线程返回结果 12 MessageBox.Show($"XXXAsync运行结束显示异步任务结果:{resuslt},ThreadId:{Thread.CurrentThread.ManagedThreadId}", "Test"); 13 } 14 15 private static async Task<string> XXXAsync() 16 { 17 await Task.Delay(1000); // 这里会导致死锁 18 //await Task.Delay(1000).ConfigureAwait(false); // 这样就不会死锁 19 20 MessageBox.Show($"XXXAsync运行结束,ThreadId:{Thread.CurrentThread.ManagedThreadId}", "XXXAsync"); 21 22 return "XXXAsync.Result"; 23 }
运行程序:
发生死锁后,无法通过关闭按钮关闭程序了;
死锁分析如下:
默认情况下,当 Wait() 未完成的 Task 时,会捕获当前线程(此时是ui主线程)上下文,在 Task(即Task.Delay(1000)) 完成时使用该ui主线程上下文恢复方法的执行。 当 async 方法内的 await 执行完成时,它会尝试获取调用者线程(这时是ui主线程)所在的上下文执行方法的剩余部分, 但是该上下文已含有一个线程(即Test方法里的task.Result等待获取异步任务结果),该线程在等待 async 方法完成。然后它们相互等待对方,然后就没有然后了,死在那里
解决方法1:使用ConfigureAwait(false)让await后面的代码尝试在线程池上下文中执行,不在ui线程的同步上下文中执行
运行结果:
解决方法2:ui线程里不去获取异步任务的结果
运行结果: