C# 等待具有不同结果的多个任务
我有3项任务:C# 等待具有不同结果的多个任务,c#,.net,async-await,task-parallel-library,.net-4.5,C#,.net,Async Await,Task Parallel Library,.net 4.5,我有3项任务: private async Task<Cat> FeedCat() {} private async Task<House> SellHouse() {} private async Task<Tesla> BuyCar() {} 专用异步任务FeedCat(){} 私有异步任务SellHouse(){} 私有异步任务BuyCar(){} 它们都需要运行,然后我的代码才能继续,我还需要每个代码的结果。所有结果都没有任何共同之处 如何调用并等
private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}
专用异步任务FeedCat(){}
私有异步任务SellHouse(){}
私有异步任务BuyCar(){}
它们都需要运行,然后我的代码才能继续,我还需要每个代码的结果。所有结果都没有任何共同之处
如何调用并等待这三项任务完成,然后获得结果?在启动所有任务后,只需分别等待这三项任务即可
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
使用
whalll
后,可以使用wait
单独拉出结果:
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;
您还可以使用
Task.Result
(因为此时您知道它们都已成功完成)。但是,我建议使用await
,因为它显然是正确的,而Result
在其他情况下可能会导致问题。您可以将它们存储在任务中,然后全部等待:
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;
您可以使用前面提到的
Task.whalll
,或者Task.WaitAll
,具体取决于您是否希望线程等待。请查看链接以了解两者的解释
使用
任务。当所有任务完成后,等待结果:
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.
如果您使用的是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);
}
}
但是,如果您想将此示例变成现实,请参阅Marc Gravell的答案,了解有关ValueTask和已完成任务的一些优化。向前警告
对于那些访问此线程和其他类似线程,寻找使用async+Wait+task工具集并行化EntityFramework的方法的人来说,这只是一个简短的开场白。:这里显示的模式是合理的,但是,当涉及到EF的特殊雪花时,除非使用单独的(新)线程,否则无法实现并行执行涉及的每个*Async()调用中的db上下文实例
由于ef db上下文固有的设计限制,禁止在同一ef db上下文实例中并行运行多个查询,因此这种事情是必要的
利用已经给出的答案,这是一种确保收集所有值的方法,即使在一个或多个任务导致异常的情况下:
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);
}
}
公共异步任务Foobar(){
等待异步任务(任务a、任务b、任务c){
返回剂量测量(等待a、等待b、等待c);
}
使用(var carTask=BuyCarAsync())
使用(var catTask=FeedCatAsync())
使用(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));//快速通道
}
cat=等待任务;
car=等待carTask;
house=等待house任务;
//或任务。等待全部(carTask、catTask、houseTask);
//或等待任务。WhenAll(carTask、catTask、houseTask);
//这取决于您如何更好地处理异常
等待返回(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);
}
}
公共异步任务Foobar(){
使用(var carTask=BuyCarAsync())
使用(var catTask=FeedCatAsync())
使用(var houseTask=SellHouseAsync())
{
cat=catTask.Status==TaskStatus.RanToCompletion?catTask.Result:(等待catTask);
car=carTask.Status==TaskStatus.RanToCompletion?carTask.Result:(等待carTask);
house=houseTask.Status==TaskStatus.RanToCompletion?houseTask.Result:(等待houseTask);
返回剂量(猫、车、房子);
}
}
给定三个任务-FeedCat()
、SellHouse()
和BuyCar()
,有两个有趣的情况:要么它们都是同步完成的(出于某种原因,可能是缓存或错误),要么不是
假设我们有,从这个问题:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// what here?
}
但是。。。这不便于处理结果;我们通常希望等待
:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
await Task.WhenAll(x, y, z);
// presumably we want to do something with the results...
return DoWhatever(x.Result, y.Result, z.Result);
}
与上面的一些注释相反,使用wait
而不是Task。whalll
对任务的运行方式(并发、顺序等)没有影响。在最高级别上,Task.whalll
早于async
/wait
的良好编译器支持,并且在这些东西不存在时非常有用。当您有一个任意的任务数组,而不是3个独立的任务时,它也很有用
但是:我们仍然存在一个问题,async
/await
为继续生成大量编译器噪声。如果任务可能实际上是同步完成的,那么我们可以通过在同步路径中构建异步回退来优化这一点:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
如果有很好的机会可以使用许多不同的返回值完全同步,则宁愿使用ValueTask
而不是Task
:
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = SellHouse();
ValueTask<Tesla> z = BuyCar();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
ValueTask DoTheThings(){
等待异步ValueTask(ValueTask a、Task b、Task c){
返回DoWhatever(等待a、等待b、等待c);
}
ValueTask x=FeedCat();
ValueTask y=SellHouse();
ValueTask z=BuyCar();
如果(x.IsCompletedSuccessfully&&
y、 我成功地完成了&&
z、 (成功完成)
返回新值任务(
DoWhatever(a.Result,b.Result,c.Result));
//我们可以安全访问。结果,正如他们所知
//完成
等待返回(x,y,z);
}
如果可能,宁愿IsCompletedSuccessfully
而不是Status==TaskStatus.RanToCompletion
;现在,它存在于.NET Core中,用于任务
,也存在于值任务
如果您试图记录所有错误,请确保
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// do something with the results...
return DoWhatever(await x, await y, await z);
}
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
Task<string> DoTheThings() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = SellHouse();
ValueTask<Tesla> z = BuyCar();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
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;
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
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()<---
var dn = await Task.WhenAll<dynamic>(FeedCat(),SellHouse(),BuyCar());
var ct = (Cat)dn[0];
class Program
{
static Stopwatch _stopwatch = new();
static async Task Main(string[] args)
{
Console.WriteLine($"fire hot");
_stopwatch.Start();
var carTask = BuyCar();
var catTask = FeedCat();
var houseTask = SellHouse();
await carTask;
await catTask;
await houseTask;
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} done!");
Console.WriteLine($"using await");
_stopwatch.Restart();
await BuyCar();
await FeedCat();
await SellHouse();
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} done!");
}
static async Task BuyCar()
{
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} buy car started");
await Task.Delay(2000);
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} buy car done");
}
static async Task FeedCat()
{
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} feed cat started");
await Task.Delay(1000);
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} feed cat done");
}
static async Task SellHouse()
{
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} sell house started");
await Task.Delay(10);
Console.WriteLine($"{_stopwatch.ElapsedMilliseconds} sell house done");
}
}
fire hot
0 buy car started
3 feed cat started
4 sell house started
18 sell house done
1004 feed cat done
2013 buy car done
2014 done!
using await
0 buy car started
2012 buy car done
2012 feed cat started
3018 feed cat done
3018 sell house started
3033 sell house done
3034 done!