使用 async/await 时出现死锁

Deadlock while using async/await

我正在尝试理解 awaitasync

效果很好。但是现在我陷入了僵局。

我用 false 调用了 ConfigureAwait,就像在 this article 中一样,但我的代码仍然阻塞。

这是我的一小段代码:

private void button1_Click(object sender, EventArgs e)
{
    var result = HeavyWorkAsync().Result;
    richTextBox1.AppendText(result);
}

private string HeavyWork()
{
    for (var index = 0; index < 1000; index++)
    {
        Task.Delay(10).Wait();
    }

    return "finished";
}

private async Task<string> HeavyWorkAsync()
{
    var task = await Task.Factory.StartNew<string>(HeavyWork).ConfigureAwait(false);
    return task;
}

阻塞的不是任务本身,而是对 Result 的调用。一个Task表示一个异步操作,但是调用它的Result属性,或者调用Wait()会阻塞当前线程直到方法returns。而且在很多情况下,它会导致死锁,因为任务无法完成,调用线程被阻塞!

为防止这种情况,使用 asyncawait

异步链接任务
private async void button1_Click(object sender, EventArgs e)
{
    var result = await HeavyWorkAsync(); // <=== await
    richTextBox1.AppendText(result);
}

此外,Task.Delay(10).Wait(); 完全违背了使用任务的初衷:这将阻塞当前线程。如果那是真的你想做的(而且这不太可能),请改为调用Thread.Sleep(10);,它会让你的意图更加清晰,你将有更少的障碍可以跳过.或者更好的是,在异步方法中使用 await Task.Delay(10);

关于ConfigureAwait

ConfigureAwait(false) 到底是做什么的?

它消除了在与任务调用者相同的上下文中将任务继续到 运行 的义务。在大多数情况下,这意味着不再保证在同一上下文中继续 运行。所以如果我有一个方法 thad Foo(),稍等一下然后 Bar() 就像这个:

async Task DoStufAsync()
{
    Foo();
    await Task.Delay(10);
    Bar(); // run in the same context as Foo()
}

我保证 Bar 会 运行 在相同的上下文中。如果我有ConfigureAwait(false),现在就不是

async Task DoStufAsync()
{
    Foo();
    await Task.Delay(10).ConfigureAwait(false);
    Bar(); // can run on another thread as Foo()
}

当您使用 ConfigureAwait(false) 时,您告诉您的程序您不介意上下文。它可以解决一些死锁问题,但通常不是正确的解决方案。正确的解决方案很可能永远不会以阻塞的方式等待任务,并且一直异步。

要扩展 Falanwe 的答案,您应该查看 Stephen Cleary's blog post。根据代码,我假设您正在使用 Windows Forms 应用程序,因此对 Task.Result 的调用将在 UI 上下文中执行任务,这反过来会阻止 UI线程。