我正在测试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;
Run Code Online (Sandbox Code Playgroud)
我想明白为什么结果总是0!为什么不是1000,2000或两个任务3000的总和?为什么不Task.WhenAll等待完成任务?
Ser*_*rvy 44
好吧,第二个是简单的,所以让我们处理那个.
对于第二项任务,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;
});
Run Code Online (Sandbox Code Playgroud)
你返回的任务的任务int,不只是一个Task的int.内部任务完成后,外部任务即"完成".当你使用时,WhenAll你不关心外部任务何时完成,你关心内部任务何时完成.有很多方法可以解决这个问题.一个是使用Unwrap.
Task<int> t1 = Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
}).Unwrap();
Run Code Online (Sandbox Code Playgroud)
现在你有了预期的,Task<int>并且WhenAll至少需要2000毫秒,正如预期的那样.您还可以添加另一个await调用来执行相同的操作:
Task<int> t1 = await Task.Factory.StartNew(async () =>
{
await Task.Delay(2000);
return 2;
});
Run Code Online (Sandbox Code Playgroud)
正如svick 在评论中所提到的另一个选择就是使用Task.Run而不是StartNew. Task.Run有一组特殊的重载方法,用于获取Func<Task<T>>和返回a Task<T>并自动为你打开它们的方法:
Task<int> t1 = Task.Run(async () =>
{
await Task.Delay(2000);
return 2;
});
Run Code Online (Sandbox Code Playgroud)
因此,Task.Run当您创建异步lambdas时,最好使用默认选项,因为它将为您"处理"此问题,尽管最好在不能使用的情况下了解它Task.Run.
最后,我们来到你没有做的选项,这是你在这种情况下应该实际做的事情.由于Task.Delay已经返回了一个Task,因此无需首先将其StartNew放入.而不是创建嵌套任务并使用Unwrap你可以不首先包装它:
var t3 = Task.Delay(3000);
await Task.WhenAll(t1, t2, t3);
Run Code Online (Sandbox Code Playgroud)
如果你真的只想等待一段固定的时间,那就是你应该做的事情.