等待Task.Run(...)对控制台和Windows应用程序的行为有所不同

Jon*_*nny 2 c# async-await

我有一个控制台应用程序与以下代码

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应用程序中我是否需要做一些不同的事情,我不需要在控制台应用程序中执行此操作?还是我从根本上误解了什么?

dca*_*tro 5

这是经常发生的事情.你造成了僵局.

简单地说,像WinForms和WPF这样的框架"强制"了一个线程同步上下文,这意味着每当你启动一个新任务时,你的其余代码将继续在它启动的同一个线程上.这样做是为了确保,例如,在UI线程上启动的代码将在任务返回后继续在同一线程上运行.

因为您正在阻塞任务(使用该Wait方法),并且任务正在尝试将值返回到该阻塞线程,所以两个线程都进入死锁状态.

在控制台应用程序中不会发生此行为,因为没有强制执行线程同步上下文,任务的继续可以在完全不同的第三个线程上运行.

Stephen Cleary在这里解释得非常好:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html