[英]Deadlock while using async/await
我试图理解await
和async
。
它运作良好。 但现在我陷入了僵局。
我用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()
将阻塞当前线程,直到该方法返回。 在很多情况下,它会导致死锁,因为任务无法完成,调用线程被阻塞!
为了防止这种情况,请使用async
和await
异步链接任务
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.