使用异步/等待时出现死锁

问题描述 投票:-2回答:2

我正在尝试理解awaitasync

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

我已经用ConfigureAwait调用了false,就像在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;
}
c# async-await deadlock
2个回答
2
投票

阻止的不是任务本身,而是对Result的调用。 Task表示异步操作,但是调用其Result属性或调用Wait()将阻塞当前线程,直到方法返回。而且在很多情况下,这会导致死锁,因为任务无法通过调用线程阻塞来完成!

[为避免这种情况,请使用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)到底是做什么的?

它消除了继续执行任务的义务,使其可以在与任务调用者相同的上下文中运行。在大多数情况下,这意味着不再保证继续可以在同一上下文中运行。因此,如果我有一种方法可以执行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)时,您告诉程序您不关心上下文。它可以解决一些死锁问题,但通常不是正确的解决方案。正确的解决方案很可能永远不会以阻塞的方式等待任务,并且一直处于异步状态。

1
投票
要扩展Falanwe的答案,您应该签出Stephen Cleary's blog post。基于代码,我假设您使用的是Windows Forms应用程序,因此对Task.Result的调用将在UI上下文中执行任务,从而又阻塞了UI线程。
© www.soinside.com 2019 - 2024. All rights reserved.