我有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 catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
注意:如果任何一个任务抛出异常,这段代码可能会在后面的任务完成之前返回异常,但它们都将运行。在几乎所有的情况下,当你已经知道结果是理想的时候就不要等待。在边缘情况下,可能不是这样。
其他回答
使用任务。然后等待结果:
var tCat = FeedCat();
var tHouse = SellHouse();
var tCar = BuyCar();
await Task.WhenAll(tCat, tHouse, tCar);
Cat cat = await tCat;
House house = await tHouse;
Tesla car = await tCar;
//as they have all definitely finished, you could also use Task.Value.
你可以使用Task。如前所述,WhenAll或Task。WaitAll,这取决于您是否希望线程等待。看看这两个解释的链接。
WaitAll vs WhenAll
提出警告
对于那些访问这个线程和其他类似线程寻找使用async+await+task工具集并行化EntityFramework的方法的人来说,这里显示的模式是合理的,然而,当涉及到EF的特殊雪花时,除非在每个* async()调用中使用单独的(新的)db-context-instance,否则无法实现并行执行。
这类事情是必要的,因为ef-db-context固有的设计限制禁止在同一个ef-db-context实例中并行运行多个查询。
利用已经给出的答案,这是确保你收集所有值的方法,即使在一个或多个任务导致异常的情况下:
public async Task<string> Foobar() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoSomething(await a, await b, await c);
}
using (var carTask = BuyCarAsync())
using (var catTask = FeedCatAsync())
using (var houseTask = SellHouseAsync())
{
if (carTask.Status == TaskStatus.RanToCompletion //triple
&& catTask.Status == TaskStatus.RanToCompletion //cache
&& houseTask.Status == TaskStatus.RanToCompletion) { //hits
return Task.FromResult(DoSomething(catTask.Result, carTask.Result, houseTask.Result)); //fast-track
}
cat = await catTask;
car = await carTask;
house = await houseTask;
//or Task.AwaitAll(carTask, catTask, houseTask);
//or await Task.WhenAll(carTask, catTask, houseTask);
//it depends on how you like exception handling better
return Awaited(catTask, carTask, houseTask);
}
}
具有或多或少相同性能特征的替代实现可以是:
public async Task<string> Foobar() {
using (var carTask = BuyCarAsync())
using (var catTask = FeedCatAsync())
using (var houseTask = SellHouseAsync())
{
cat = catTask.Status == TaskStatus.RanToCompletion ? catTask.Result : (await catTask);
car = carTask.Status == TaskStatus.RanToCompletion ? carTask.Result : (await carTask);
house = houseTask.Status == TaskStatus.RanToCompletion ? houseTask.Result : (await houseTask);
return DoSomething(cat, car, house);
}
}
你可以把它们存储在任务中,然后等待它们:
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;
如果您试图记录所有错误,请确保您保留任务。代码中的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中的任务异常处理