有人可以解释一下吗,也许我遗漏了一些明显的东西。
这两个案例在行为上看似相同,但其实不然。
案例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 Preview,如果这很重要的话,它可能使用 Roslyn 进行编译。
答案就在这里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); });
第一个按预期创建任务。第二个创建一个任务,其返回类型是等待2000ms的任务。第一个示例之所以有效,是因为以下重载...
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();
不鼓励将
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<Task>(async () => await Task.Delay(2000));
t.Unwrap().Wait();
使用
Task.Run
更简单,可以为您完成所有操作,包括 Unwrap
,因此您可以返回代表整个 Task
操作的 async
作为回报。
Aron 的回答并非100%正确。如果您使用建议的修复,您最终会得到:
不能在 Promise 风格的任务上调用 Start。
就我而言,我通过保存对包装任务和未包装任务的引用解决了类似的问题。您可以使用
wrappedTask.Start()
启动任务,但使用 unwrappedTask.Wait()
等待任务(或访问与任务执行相关的任何内容,例如其 TaskStatus
)。
(来源)