使用异步/等待时发生死锁 [英] Deadlock while using async/await
问题描述
我试图理解await
和async
.
效果很好.但是现在我陷入了僵局.
It works very well. But now I have a deadlock.
I've called ConfigureAwait
with false
, like in this article, but my code is still blocking.
这是我的代码的一小段:
Here's a little snippet of my 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;
}
推荐答案
阻止的不是任务本身,而是对Result
的调用. Task
表示异步操作,但是调用其Result
属性或调用Wait()
将阻塞当前线程,直到方法返回.而且在很多情况下,它将导致死锁,因为任务无法通过调用线程阻塞来完成!
What's blocking is not the task itself, it's the call to Result
. A Task
represents an asynchronous operation, but calling its Result
property, or calling Wait()
will block the current thread until the method returns. And in a lot of cases, it will cause a deadlock because the task is not able to complete with it's calling thread blocked!
为防止这种情况,请使用async
和await
To prevent that, chain the tasks asynchronously, using async
and 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);
.
Also, Task.Delay(10).Wait();
completely defeats the prupose of using tasks in the first place: that will block the current thread. If that's really what you want to do (and it's pretty unlikely), call Thread.Sleep(10);
instead, it will make your intent much clearer, and you will have less hoops to jump through. Or better, use await Task.Delay(10);
in an async method.
ConfigureAwait(false)
到底做什么?
它消除了继续执行任务的义务,使其可以在与任务调用者相同的上下文中运行.在大多数情况下,这意味着不再保证继续可以在同一上下文中运行.因此,如果我有方法Foo()
,则要稍等一下Bar()
:
It removes the obligation for the continuation of the task to run in the same context as the caller of the task. In most cases that means that the continuation is no longer guaranteed to run on the same context. So if I have a method thad does Foo()
, waits a little then Bar()
like this one:
async Task DoStufAsync()
{
Foo();
await Task.Delay(10);
Bar(); // run in the same context as Foo()
}
我保证Bar将在相同的上下文中运行.如果我有ConfigureAwait(false)
,就不再是这种情况了
I'm guaranteed Bar will run in the same context. If I had ConfigureAwait(false)
, it's no longer the case
async Task DoStufAsync()
{
Foo();
await Task.Delay(10).ConfigureAwait(false);
Bar(); // can run on another thread as Foo()
}
使用ConfigureAwait(false)
时,您告诉程序您不介意上下文.它可以解决一些僵局的问题,但通常不是正确的解决方案.正确的解决方案很可能永远不会以阻塞的方式等待任务,并且一直处于异步状态.
When you're using ConfigureAwait(false)
, you tell your program you dont mind about the context. It can solve some deadlocking problems, but isn't usually the right solution. The right solution is most likely never to wait for tasks in a blocking way, and being asynchronous all the way.
这篇关于使用异步/等待时发生死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!