C# 为什么这个异步方法不';不会导致线程死锁吗?
我需要一个解释,为什么这段代码不会在它运行的线程上造成死锁 (这是一个WinForm应用程序,在单击按钮时发生):C# 为什么这个异步方法不';不会导致线程死锁吗?,c#,async-await,task,deadlock,C#,Async Await,Task,Deadlock,我需要一个解释,为什么这段代码不会在它运行的线程上造成死锁 (这是一个WinForm应用程序,在单击按钮时发生): asyncMethod().Wait()应该在等待另一个任务完成时阻止fakeMainThread任务的线程,并且由于在fakeMainThread中也发生了等待,因此它不应该能够“等待”,并且fakeMainThread上应该出现死锁。但这并没有发生,我看到botMessageBox.Show(“AsyncMethodFinished”)和MessageBox.Show(“Fak
asyncMethod().Wait()
应该在等待另一个任务完成时阻止fakeMainThread任务的线程,并且由于在fakeMainThread中也发生了等待
,因此它不应该能够“等待”,并且fakeMainThread上应该出现死锁。但这并没有发生,我看到botMessageBox.Show(“AsyncMethodFinished”)代码>和MessageBox.Show(“FakeMainThread finished”)代码>消息。
注意:如果我这样说:
async Task asyncMethod()
{
await Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock appears in Main Thread (UI)
MessageBox.Show("FakeMainThread finished");
在主按钮中,UI上显示directli死锁的内部。谢谢 在第一个代码示例中,由于SynchronizationContext
,您不会遇到死锁。此上下文说明了await必须执行哪些操作才能恢复代码。启动新任务(task.Run
)时,将获得默认上下文。在按钮1中,单击可从表单中获取上下文
表单的上下文只允许执行单个线程(用于绘制/更新表单的线程)
当您调用.Wait()
时,您将保持线程“锁定”,等待任务完成。这意味着线程不会被释放用于另一个作业,这也是表单进入“未响应”状态的原因之一
当您执行一个等待[code]
,并且该代码已完成时,它将要求同步上下文安排代码的其余部分
- 在默认上下文中,任何空闲线程都被占用,代码将继续,任务标记为已完成,因此
.Wait()
中的“循环”将收到任务已完成并继续的信号
- 对于表单上下文,只有一个线程,因此此任务的完成计划在当前代码完成后运行。这就是僵局的原因
避免此死锁是您经常收到使用ConfigureAwait(false)
的建议的主要原因,这告诉await
它应该用默认的同步上下文替换当前的同步上下文,从而允许您使用任何空闲线程。
您不想使用此选项(或者说ConfigureAwait(true)
)的唯一原因是您需要更新表单上的某些内容(WPF或WinForms),或者您需要http上下文(ASP.NET,而不是ASP.NET核心)。这是因为只有一个线程可以访问表单或http上下文。在这里,您的消息框上不会出现异常。Show
,因为这是表单上下文的“外部”,不需要这个特殊的线程/同步上下文
有关更多信息,请参阅。由于同步上下文的原因,您在第一个代码示例中没有遇到死锁。此上下文说明了await必须执行哪些操作才能恢复代码。启动新任务(task.Run
)时,将获得默认上下文。在按钮1中,单击可从表单中获取上下文
表单的上下文只允许执行单个线程(用于绘制/更新表单的线程)
当您调用.Wait()
时,您将保持线程“锁定”,等待任务完成。这意味着线程不会被释放用于另一个作业,这也是表单进入“未响应”状态的原因之一
当您执行一个等待[code]
,并且该代码已完成时,它将要求同步上下文安排代码的其余部分
- 在默认上下文中,任何空闲线程都被占用,代码将继续,任务标记为已完成,因此
.Wait()
中的“循环”将收到任务已完成并继续的信号
- 对于表单上下文,只有一个线程,因此此任务的完成计划在当前代码完成后运行。这就是僵局的原因
避免此死锁是您经常收到使用ConfigureAwait(false)
的建议的主要原因,这告诉await
它应该用默认的同步上下文替换当前的同步上下文,从而允许您使用任何空闲线程。
您不想使用此选项(或者说ConfigureAwait(true)
)的唯一原因是您需要更新表单上的某些内容(WPF或WinForms),或者您需要http上下文(ASP.NET,而不是ASP.NET核心)。这是因为只有一个线程可以访问表单或http上下文。在这里,您的消息框上不会出现异常。Show
,因为这是表单上下文的“外部”,不需要这个特殊的线程/同步上下文
有关更多信息,请参阅。为什么不“等待”的线程池线程(它只是在调用Wait()
)死锁?它没有触及UI线程(通过同步上下文),因此您没有阻止它-为什么它会在意?为什么不“等待”的线程池线程应该等待(它只是调用Wait()
)死锁?它没有触及UI线程(通过同步上下文),所以你没有阻止它-为什么它会在意?“当你调用.Wait()时,你基本上会要求你的代码运行一个常量循环”,这就是为什么我注意到“简化了,它不仅仅是这个”。这是一个实现细节,结果是一样的,只是性能更好,而且我所说的对一些人来说可能更容易理解,但感谢你的进一步解释:)老实说,我认为一个误导性的解释是不值得的,即使它更容易理解。任何人如果不知道等待是如何工作的,并把你的答案作为信息来源,就会学到一些以后不得不忘记的东西。我建议把这个有问题的部分从你的回答中去掉
async Task asyncMethod()
{
await Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock appears in Main Thread (UI)
MessageBox.Show("FakeMainThread finished");