繁体   English   中英

使用 async/await 时出现死锁

[英]Deadlock while using async/await

我试图理解awaitasync

它运作良好。 但现在我陷入了僵局。

我用false调用了ConfigureAwait ,就像在这篇文章中一样,但我的代码仍然阻塞。

这是我的代码的一小段:

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()将阻塞当前线程,直到该方法返回。 在很多情况下,它会导致死锁,因为任务无法完成,调用线程被阻塞!

为了防止这种情况,请使用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) ,您告诉您的程序您不介意上下文。 它可以解决一些死锁问题,但通常不是正确的解决方案。 正确的解决方案很可能永远不会以阻塞方式等待任务,并且一直是异步的。

要扩展 Falanwe 的答案,您应该查看Stephen Cleary 的博客文章 基于代码,我假设您使用的是 Windows 窗体应用程序,因此对 Task.Result 的调用将在 UI 上下文上执行任务,这又会阻止 UI 线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM