我有一个控制台应用程序与以下代码
static void Main(string[] args) {
Method();
Console.ReadKey();
}
private static void Method() {
Task t = DoSomething();
t.Wait();
Console.WriteLine("Finished");
}
private static async Task DoSomething() {
await Task.Run(() =>
Thread.Sleep(1000));
}
Run Code Online (Sandbox Code Playgroud)
一切都按照我的预期运行,控制台在运行程序后一秒显示"已完成".当我将相同的代码移动到Windows应用程序中时,我发现程序就停止了
private void button1_Click(object sender, EventArgs e) {
Task t = DoSomething();
t.Wait();
MessageBox.Show("Finished");
}
private static async Task DoSomething() {
await Task.Run(() =>
Thread.Sleep(1000));
}
Run Code Online (Sandbox Code Playgroud)
调试器显示当前执行的行是t.Wait(),即使在运行DoSomething方法中的Task之后也是如此.
在Windows应用程序中我是否需要做一些不同的事情,我不需要在控制台应用程序中执行此操作?还是我从根本上误解了什么?
这是经常发生的事情.你造成了僵局.
简单地说,像WinForms和WPF这样的框架"强制"了一个线程同步上下文,这意味着每当你启动一个新任务时,你的其余代码将继续在它启动的同一个线程上.这样做是为了确保,例如,在UI线程上启动的代码将在任务返回后继续在同一线程上运行.
因为您正在阻塞任务(使用该Wait方法),并且任务正在尝试将值返回到该阻塞线程,所以两个线程都进入死锁状态.
在控制台应用程序中不会发生此行为,因为没有强制执行线程同步上下文,任务的继续可以在完全不同的第三个线程上运行.
Stephen Cleary在这里解释得非常好:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html