作为一项学习练习,我试图重现以常规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);
}
}
Run Code Online (Sandbox Code Playgroud)
我最好奇是否有人知道为什么会这样?
发生这种情况是因为这WindowsFormsSynchronizationContext取决于标准 Windows 消息循环的存在。控制台应用程序不会启动这样的循环,因此WindowsFormsSynchronizationContext不会处理发布到 的消息,不会调用任务继续,因此程序挂在第一个await. 您可以通过查询 boolean 属性来确认消息循环不存在Application.MessageLoop。
获取一个值,该值指示该线程上是否存在消息循环。
要使其WindowsFormsSynchronizationContext正常工作,您必须启动消息循环。可以这样做:
static void Main(string[] args)
{
EventHandler idleHandler = null;
idleHandler = async (sender, e) =>
{
Application.Idle -= idleHandler;
await MyMain(args);
Application.ExitThread();
};
Application.Idle += idleHandler;
Application.Run();
}
Run Code Online (Sandbox Code Playgroud)
该MyMain方法是您当前的Main方法,已重命名。
更新:实际上该Application.Run方法会自动WindowsFormsSynchronizationContext在当前线程中安装 a ,因此您不必显式执行此操作。如果您希望阻止此自动安装,请WindowsFormsSynchronizationContext.AutoInstall在调用之前配置该属性Application.Run。
该
AutoInstall属性确定是否WindowsFormsSynchronizationContext在创建控件时或启动消息循环时安装。