使用异步等待时的死锁

时间:2015-03-24 11:21:00

标签: c# async-await deadlock

我正在尝试了解await和async。 它工作得很好。但现在我陷入僵局。我将ConfigureAwait称为false,如本文所述:http://blog.ciber.no/2014/05/19/using-task-configureawaitfalse-to-prevent-deadlocks-in-async-code/ 但我的代码仍在阻止。 这是我的代码中的一小部分:

 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;
    }

2 个答案:

答案 0 :(得分: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)究竟做了什么?

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

答案 1 :(得分:1)

要扩展Falanwe的答案,您应该查看Stephen Cleary's blog post。基于代码,我假设您使用的是Windows窗体应用程序,因此对Task.Result的调用将在UI上下文中执行任务,从而阻止UI线程。