我正在尝试理解await
和async
。
效果很好。但是现在我陷入了僵局。
我已经用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;
}
阻止的不是任务本身,而是对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)
时,您告诉程序您不关心上下文。它可以解决一些死锁问题,但通常不是正确的解决方案。正确的解决方案很可能永远不会以阻塞的方式等待任务,并且一直处于异步状态。