我有3个任务:
private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}
它们都需要在我的代码继续之前运行,我也需要每个结果。这些结果之间没有任何共同之处
我如何调用和等待3个任务完成,然后得到结果?
我有3个任务:
private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}
它们都需要在我的代码继续之前运行,我也需要每个结果。这些结果之间没有任何共同之处
我如何调用和等待3个任务完成,然后得到结果?
当前回答
var dn = await Task.WhenAll<dynamic>(FeedCat(),SellHouse(),BuyCar());
如果你想访问Cat,你可以这样做:
var ct = (Cat)dn[0];
这是非常简单的做法和非常有用的使用,没有必要去追求一个复杂的解决方案。
其他回答
你可以把它们存储在任务中,然后等待它们:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
Cat cat = await catTask;
House house = await houseTask;
Car car = await carTask;
只需要分别等待这三个任务,在启动它们之后:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
注意:如果任何一个任务抛出异常,这段代码可能会在后面的任务完成之前返回异常,但它们都将运行。在几乎所有的情况下,当你已经知道结果是理想的时候就不要等待。在边缘情况下,可能不是这样。
如果你正在使用c# 7,你可以使用一个方便的包装方法,像这样…
public static class TaskEx
{
public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> task1, Task<T2> task2)
{
return (await task1, await task2);
}
}
...当您希望等待多个具有不同返回类型的任务时,可以启用这样的方便语法。当然,您必须对等待的不同数量的任务进行多次重载。
var (someInt, someString) = await TaskEx.WhenAll(GetIntAsync(), GetStringAsync());
但是,如果您打算把这个例子变成现实,请参阅Marc Gravell对ValueTask和已经完成的任务的一些优化的回答。
var dn = await Task.WhenAll<dynamic>(FeedCat(),SellHouse(),BuyCar());
如果你想访问Cat,你可以这样做:
var ct = (Cat)dn[0];
这是非常简单的做法和非常有用的使用,没有必要去追求一个复杂的解决方案。
如果您试图记录所有错误,请确保您保留任务。代码中的WhenAll行,很多注释建议您可以删除它并等待单个任务。的任务。WhenAll对于错误处理非常重要。如果没有这一行,您可能会为未观察到的异常打开代码。
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
假设FeedCat在以下代码中抛出异常:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
在这种情况下,您将永远不会等待houseTask或carTask。这里有3种可能的场景:
SellHouse is already completed successfully when FeedCat failed. In this case you are fine. SellHouse is not complete and fails with exception at some point. Exception is not observed and will be rethrown on finalizer thread. SellHouse is not complete and contains awaits inside it. In case your code runs in ASP.NET SellHouse will fail as soon as some of the awaits will completed inside it. This happens because you basically made fire & forget call and synchronization context was lost as soon as FeedCat failed.
下面是你将得到的case(3)的错误:
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
at System.Threading.Tasks.Task.Execute()
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
at System.Threading.Tasks.Task.Execute()<---
对于情况(2),您将得到类似的错误,但原始异常堆栈跟踪。
对于. net 4.0及更高版本,您可以使用TaskScheduler.UnobservedTaskException捕获未观察到的异常。对于。net 4.5及以后的版本,未观察到的异常将被默认接受,而对于。net 4.0,未观察到的异常将使您的进程崩溃。
更多详细信息请参见。net 4.5中的任务异常处理