我使用异步和等待遇到了奇怪的行为。如果我尝试等待手动创建的任务T1,而该任务本身应该等待另一个任务T2,则即使任务T2仍在等待,任务T1也已经运行完毕。
为了说明这个问题,我写了一些代码。当我从此示例运行“ task1”时,输出为:
task1运行完毕 正在更新...
当我改为运行'task2'时,输出始终为:
更新中... task2运行完毕
有人解释为什么第一个等待表达式不依赖于内部等待吗?
private void OnLoaded(object sender, RoutedEventArgs e)
{
var task1 = new Task(async () => await UpdateAfterDelay());
task1.Start();
await task1;
Console.WriteLine("task1 ran to completion");
var task2 = Task.Run(async () => await UpdateAfterDelay());
await task2;
Console.WriteLine("task2 ran to completion");
}
private async Task UpdateAfterDelay()
{
await Task.Delay(2000);
Console.WriteLine("updating...");
}
答案 0 :(得分:4)
Daniel有正确的想法。如果您查看constructors for Task
,它们都只接受Action
对象,它们实际上是void
方法。这意味着您的匿名方法被解释为async void
,即“即发即弃”(启动它,别等它了)。
如果您不使用匿名方法,这将变得更加清楚:
var task1 = new Task(UpdateAfterDelayTask); //this does not compile
var task2 = new Task(UpdateAfterDelayVoid); //this does
private async Task UpdateAfterDelayTask()
{
await Task.Delay(2000);
}
private async void UpdateAfterDelayVoid()
{
await Task.Delay(2000);
}
task1
分配抱怨您提供的方法的返回类型错误。
Task.Run
,但是,has an overload接受Func<Task>
(一种返回Task
的方法)。因此,编译器检查您的匿名方法,发现它返回了Task
,并选择了Func<Task>
重载。 returns a new Task
that depends on the Task
returned by your anonymous method过载。
所有这些,也许是由于您未共享而使用new Task
或Task.Run
的原因,但实际上并不需要。您可以这样做:
var task1 = UpdateAfterDelay();
// do something else
await task1;
如果您不是在“做其他事情”,那么您根本不需要task1
变量。只是await UpdateAfterDelay()
。
值得一提的是this article解释了为什么几乎不需要使用new Task()
,而this later article(在Task.Run
出来之后就写了)解释了为什么您几乎总是想要使用Task.Run()
(如果需要的话)。
答案 1 :(得分:2)
我想这里发生的是,由于Task
类不包含接受a的构造函数,因此传递给Action
构造函数的异步lambda被隐式转换为Task
。 Func<Task>
,这就是您想要的。因此,它像是Action
一样执行-并立即返回。在IntelliSense中检查构造函数中使用的类型以确认这一点。
答案 2 :(得分:-1)
其他答案都有正确的主意,但可以进行总结和简化:
使用
var task1 = new Task(...)
task1.Start();
使用异步lambda创建一个空任务,该任务在后台运行并立即返回(因此await task1
实际上不需要等待)
但使用
var task2 = Task.Run(...)
创建一个仍在后台运行的等待任务,但实际上将返回一个await
可以等待的值。