比如下面的代码,去掉 Task.WhenAll 也能正常运行,请问使用 Task.WhenAll 的好处是什么?
var getHeadlines = this.RenderViewComponentToStringAsync(typeof(Headline));
var aggTopPosts = this.RenderViewComponentToStringAsync(typeof(AggTopPosts));
await Task.WhenAll(getHeadlines, aggTopPosts);
var result = new
{
Headlines = await getHeadlines,
AggTopPosts = await aggTopPosts
};
在 stackoverflow 上找到了正确答案 https://stackoverflow.com/a/67506995
The behavior is just slightly different in the case of exceptions when both calls fail. If they're awaited one at a time, the second failure will never be observed; the exception from the first failure is propagated immediately. If using Task.WhenAll, both failures are observed; one exception is propagated after both tasks fail.
简而言之,使用 Task.WhenAll 可以在发生异常时拿到完整的异常信息
前者是并行的,后者是串行的?
public static async Task<int> GetInt1()
{
await Task.Delay(2000);
Console.WriteLine("From GetInt1");
return await Task.FromResult(1);
}
public static async Task<int> GetInt2()
{
await Task.Delay(2000);
Console.WriteLine("From GetInt2");
return await Task.FromResult(2);
}
public static async Task ForWhenAll()
{
await Task.WhenAll(GetInt1(), GetInt2());
}
public static async Task ForNewObj()
{
var dd = new
{
i1 = await GetInt1(),
i2 = await GetInt2()
};
}
执行ForNewObj
要花费2s+2s,执行ForWhenAll
只要2s
确实 我的demo有问题,实际效果确实是没差异
不加 WhenAll 就是 FireAndForgot 模式吧,如果不关注结果和异常处理,在webapi中应该没有问题,单纯 console 可能会导致,程序关闭时没有执行完就凉了。
此代码在效果上没有任何差异.
var getHeadlines = this.RenderViewComponentToStringAsync(typeof(Headline));
var aggTopPosts = this.RenderViewComponentToStringAsync(typeof(AggTopPosts));
// 此时此刻 的getHeadlines和aggTopPosts 所代表的task已经启动了.
await Task.WhenAll(getHeadlines, aggTopPosts);
var result = new
{
Headlines = await getHeadlines,
AggTopPosts = await aggTopPosts
};
1楼的case 转换后的代码应该是
Task<any> getHeadlines()=> this.RenderViewComponentToStringAsync(typeof(Headline));
Task<any> aggTopPosts()=> this.RenderViewComponentToStringAsync(typeof(AggTopPosts));
await Task.WhenAll(getHeadlines(), aggTopPosts());
var result = new
{
Headlines = await getHeadlines(),
AggTopPosts = await aggTopPosts()
};
另外:
await Task.WhenAll(getHeadlines, aggTopPosts);
// 此时的2个task已经完成了.
var result = new
{
Headlines = await getHeadlines, // 这里的代码等于是直接从task拿result, 没有任何等待.
AggTopPosts = await aggTopPosts
};
以下示例创建一组任务,用于对数组中的 URL 执行 ping 操作。 任务存储在转换为数组并传递给方法的集合中List<Task>WhenAll(IEnumerable<Task>)。 在调用 Wait 该方法可确保所有线程都已完成之后,该示例将检查 Task.Status 该属性以确定是否有任何任务出错。
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static async Task Main()
{
int failed = 0;
var tasks = new List<Task>();
String[] urls = { "www.adatum.com", "www.cohovineyard.com",
"www.cohowinery.com", "www.northwindtraders.com",
"www.contoso.com" };
foreach (var value in urls) {
var url = value;
tasks.Add(Task.Run( () => { var png = new Ping();
try {
var reply = png.Send(url);
if (! (reply.Status == IPStatus.Success)) {
Interlocked.Increment(ref failed);
throw new TimeoutException("Unable to reach " + url + ".");
}
}
catch (PingException) {
Interlocked.Increment(ref failed);
throw;
}
}));
}
Task t = Task.WhenAll(tasks.ToArray());
try {
await t;
}
catch {}
if (t.Status == TaskStatus.RanToCompletion)
Console.WriteLine("All ping attempts succeeded.");
else if (t.Status == TaskStatus.Faulted)
Console.WriteLine("{0} ping attempts failed", failed);
}
}
// The example displays output like the following:
// 5 ping attempts failed
好处是方便批量启动任务和批量等待任务完成。每个Task都直接await会导致任务被串行化,前一个完成后一个才启动,如果后续任务需要前置任务的结果才能开始当然没问题。如果多个任务没有依赖关系,批量启动时就不能await。需要先用变量保存Task,全部启动后再await变量,变量太多的话代码非常难看(var t1 = Async1(); var t2 = Async2(); var t3 = Async3(); ……; await t1; await t2; await t3; ……;
),如果任务是在循环里启动的甚至不知道应该准备多少变量,这时使用Task.WhenAll就非常方便了。