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

时间:2013-11-21 14:09:17

标签: 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));
}

一切都按照我的预期运行,控制台在运行程序后一秒钟显示“已完成”。当我将相同的代码移动到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));
}

调试器显示当前执行的行是t.Wait(),即使在运行了DoSomething方法中的Task之后也是如此。

我是否需要在Windows应用程序中执行某些不同的操作,而我不需要在控制台应用程序中执行此操作?还是我从根本上误解了什么?

1 个答案:

答案 0 :(得分:5)

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

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

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

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

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