2012-12-28 219 views
21

我测试了async,我发现这个情况,我无法理解Task.Delay不`吨的工作:为什么在这种情况下

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等待完成任务?

+0

@GrantThomas我想分配给一个变量,但它给了我一个编译器错误,我该怎么做? – MuriloKunze

+0

@MuriloKunze为了回答这个问题,我们需要看到代码行和错误。 – Servy

+0

我试过这个:var taskresult = await Task.WhenAll(t1,t2); 但它给了我一个'不能分配void的隐式类型局部变量'。 – MuriloKunze

回答

41

好的,所以,第二个是简单的,所以让我们来处理那个。

对于第二项任务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; 
}); 

你回不只是intTaskint任务的任务。内部任务完成后,外部任务将“完成”。当您使用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; 
}); 

正如svick in a comment提到的另一种选择是只用Task.Run,而不是StartNewTask.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); 

如果你真的只是想等待这是你应该做的事情的固定时间量。

+0

+1为伟大的答案 –

+0

伟大的答案:) – MuriloKunze

+4

还有另一种选择:使用'Task.Run()'。它自己解开任务,所以它可能比使用'StartNew()'和'Unwrap()'结合起来更好。 – svick

相关问题