我正在测试 async
,我发现了这种我无法理解的情况:
var watch = Stopwatch.StartNew();
var t1 = Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
});
var t2 = Task.Factory.StartNew(() =>
{
Task.Delay(1000);
return 1;
});
await Task.WhenAll(t1, t2);
var result = watch.ElapsedMilliseconds;
我很想明白为什么结果总是0!为什么不是 1000、2000 或两个任务的总和 3000?为什么 Task.WhenAll
不等待任务完成?
最佳答案
好吧,那么,第二个比较简单,所以让我们来处理那个。
对于第二个任务,t2
, 你不对 Task.Delay(1000)
的结果做任何事情.你不await
它,你不Wait
它等。鉴于该方法不是 async
我以为你的意思是它是一个阻塞等待。为此,您需要添加 Wait()
到 Delay
的结尾调用使其成为阻塞等待,或者只使用 Thread.Sleep()
.
对于第一个任务,您被使用 var
的事实所困扰。 .当你不使用 var
时发生的事情会更清楚:
Task<Task<int>> t1 = Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
});
您正在返回 int
的任务中的一个任务,不只是一个 Task
的 int
.一旦内部任务完成启动,外部任务就“完成”了。当您使用 WhenAll
您不关心外部任务何时完成,您关心的是内部 任务何时完成。有多种方法可以处理此问题。一种是使用 Unwrap
.
Task<int> t1 = Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
}).Unwrap();
现在您有了预期的 Task<int>
和 WhenAll
正如预期的那样,至少需要 2000 毫秒。您还可以添加另一个 await
调用做同样的事情:
Task<int> t1 = await Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
});
正如斯维克所说 in a comment另一种选择是只使用 Task.Run
而不是 StartNew
. Task.Run
对于采用 Func<Task<T>>
的方法有一组特殊的重载并返回 Task<T>
并自动为您打开它们:
Task<int> t1 = Task.Run(async () =>
{
await Task.Delay(2000);
return 2;
});
因此,最好使用 Task.Run
作为创建异步 lambda 时的默认选项,因为它会为你“处理”这个问题,尽管在你不能使用 Task.Run
的复杂情况下最好注意它。 .
最后,我们讨论了您没有做的选项,这可能是您在这种情况下实际应该做的。自 Task.Delay
已经返回一个任务,没有必要把它放在 StartNew
中首先。而不是创建嵌套任务并使用 Unwrap
你不能一开始就把它包起来:
var t3 = Task.Delay(3000);
await Task.WhenAll(t1, t2, t3);
如果您实际上只想等待一段固定的时间,那么您应该这样做。
关于c# - 为什么 Task.Delay 在这种情况下不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14071606/