c# - 在控制台应用程序中使用 WindowsFormsSynchronizationContext 时出现异步/等待死锁
问题描述
作为一项学习练习,我正在尝试重现在普通 Windows 窗体中发生的异步/等待死锁,但使用控制台应用程序。我希望下面的代码会导致这种情况发生,确实如此。但是在使用 await 时也会意外发生死锁。
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
static class Program
{
static async Task Main(string[] args)
{
// no deadlocks when this line is commented out (as expected)
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
Console.WriteLine("before");
//DoAsync().Wait(); // deadlock expected...and occurs
await DoAsync(); // deadlock not expected...but also occurs???
Console.WriteLine("after");
}
static async Task DoAsync()
{
await Task.Delay(100);
}
}
我很好奇是否有人知道为什么会这样?
解决方案
WindowsFormsSynchronizationContext
会将其给定的任何委托发布到由 UI 线程提供服务的 WinForms 消息循环。但是,您从未设置其中之一,也没有 UI 线程,因此您发布的任何内容都会消失。
所以你await
正在捕获一个SynchronizationContext
永远不会运行任何完成的东西。
正在发生的事情是:
- 您
Task
正在从Task.Delay
Task
主线程使用自旋锁(inTask.SpinThenBlockingWait
)同步启动等待此操作完成- 自旋锁超时,主线程创建一个等待事件,由Task上的一个continuation设置
- Task 完成(你可以看到它已经完成,因为它的 Status 是 RanToCompletion)
- 任务尝试完成将释放主线程正在等待的事件的延续(
Task.FinishContinuations
)。这最终会调用TaskContinuation.RunCallback
(尽管我还没有追踪到那个调用路径),它调用了你的WindowsFormSynchronizationContext.Post
. - 但是,
Post
什么都不做,就会发生死锁。
为了获得这些信息,我做了以下事情:
- 尝试调用
new WindowsFormsSynchronizationContext.Post(d => ..., null)
,看看委托没有被调用。 - 构建我自己的
SynchronizationContext
并安装它,看看什么时候Post
被调用。 - 在死锁期间打破调试器,查看
Threads
并查看Call Stack
主线程。 - 在变量中捕获正在等待的任务,在监视窗口中查看它,右键单击 -> 生成对象 ID,然后将该对象 ID 放入监视窗口中。让它死锁、中断,并根据其对象 ID 在监视窗口中检查任务。
推荐阅读
- hadoop - 我无法访问 Hadoop Web 界面(DataNode、ResourceManager)
- json - 编组时间。到 unix 时间戳的时间
- python - Ubuntu 升级到 18.04 后,keras 和 tensorflow 中的 GPU 变慢
- android - 给定 4 个点的坐标,在谷歌地图 API 上创建方形网格
- r - 使用嵌套的 for 循环填充距离矩阵
- r - FlowJo v10.0 中的黑桃
- mysql - 同一实体的多个表示正在合并错误
- ansible - 无法创建 %UserProfile% 目录
- macos - 如何设置Tcl/Tk“系统配置”项?
- php - Laravel 中的表单提交错误:“找不到对象”