有人可以解释一下,也许我错过了一些明显的东西。
这两个案例的行为似乎相同,但事实并非如此。
案例1 :
使用async
Action,
启动任务,该任务在一段时间内有效:
var t = Task.Run(async () => { await Task.Delay(2000); });
第二个任务等待第一个任务:
var waitingTask = Task.Run(() => { t.Wait(); });
等待第二项任务:
waitingTask.Wait();
案例2 :
使用Task
构造函数构建Task
,传递相同的async
Action
:
var t = new Task(async () => { await Task.Delay(2000); });
启动另一项任务以等待第一项任务(就像第一种情况一样):
var waitingTask = Task.Run(() => { t.Wait(); });
开始第一项任务:
t.Start();
等待第二项任务:
waitingTask.Wait();
第一种情况表现如预期:等待任务在第一种情况结束后,在2秒之后结束。
第二种情况很奇怪:等待任务很快就会结束,早在第一种情况发生之前。
从两个任务打印消息时很容易看到。第二个任务结束时的打印将显示差异。
我正在使用VS 2015预览版,如果这很重要,可能会使用Roslyn进行编译。
答案 0 :(得分:8)
答案在http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
实际上问题在于new Task
,并且只有Action
,而您尝试提供Func<Task>
。以下是前两行,但使用正确的重载进行了重写。
Task t = Task.Run(async () => { await Task.Delay(2000); });
和
Task<Task> t = new Task<Task>(async () => { await Task.Delay(2000); });
第一个按预期创建任务。第二个创建一个任务,谁的返回类型是一个等待2000毫秒的任务。第一个例子起作用,因为以下重载...
public Task Run(Func<Task>);
public Task<T> Run(Func<T>);
public Task<T> Run(Func<Task<T>>);
etc...
这些重载旨在为您自动调用Task<Task<T>>.Unwrap()
。
结果是在你的第二个例子中,你实际上正在等待第一个任务的开始/排队。
您可以通过
修复第二个示例var t = new Task<Task>(async () => { await Task.Delay(2000); }).Unwrap();
答案 1 :(得分:3)
不建议Task
使用async-await
构造函数,您应该只使用Task.Run
:
var t = Task.Run(async () => await Task.Delay(2000));
Task
构造函数是在[基于任务的异步模式(TAP)] [1]和async-await
之前构建的。它将返回一个只启动内部任务并继续前进而不等待它完成的任务(即点火并忘记)。
您可以通过将所需结果指定为Task<Task>
来解决此问题,并使用Unwrap
创建代表整个操作的任务,包括内部任务:
var t = new Task(async () => await Task.Delay(2000));
t.Unwrap().Wait();
使用Task.Run
更简单,并为您完成所有这些操作,包括Unwrap
,以便您返回代表整个Task
操作的async
。
答案 2 :(得分:1)
Aron的回答不是100%正确。如果您使用建议的修补程序,最终会得到:
启动可能不会在承诺式任务上调用。
在我的情况下,我通过保持对包装和未包装任务的引用来解决类似的问题。您使用wrappedTask.Start()
来启动任务,但unwrappedTask.Wait()
等待它(或访问与任务执行相关的任何内容,例如TaskStatus
)。
(Source)