首页 新闻 会员 周边 捐助

C#: 使用 Task.WhenAll 的好处是什么

0
悬赏园豆:100 [已解决问题] 解决于 2023-10-21 10:36

比如下面的代码,去掉 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
};
dudu的主页 dudu | 高人七级 | 园豆:30948
提问于:2023-09-26 07:49
< >
分享
最佳答案
0

在 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 可以在发生异常时拿到完整的异常信息

dudu | 高人七级 |园豆:30948 | 2023-10-21 10:36
其他回答(5)
0

前者是并行的,后者是串行的?

        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

收获园豆:20
复制粘贴机器人 | 园豆:720 (小虾三级) | 2023-09-26 08:55

确实 我的demo有问题,实际效果确实是没差异

支持(0) 反对(0) 复制粘贴机器人 | 园豆:720 (小虾三级) | 2023-09-26 23:58
0

不加 WhenAll 就是 FireAndForgot 模式吧,如果不关注结果和异常处理,在webapi中应该没有问题,单纯 console 可能会导致,程序关闭时没有执行完就凉了。

收获园豆:20
huiyuanai709 | 园豆:487 (菜鸟二级) | 2023-09-26 13:46
0

此代码在效果上没有任何差异.

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()
};
收获园豆:20
czd890 | 园豆:14488 (专家六级) | 2023-09-26 17:39

另外:

await Task.WhenAll(getHeadlines, aggTopPosts);
// 此时的2个task已经完成了.

var result = new
{
    Headlines = await getHeadlines, // 这里的代码等于是直接从task拿result, 没有任何等待.
    AggTopPosts = await aggTopPosts
};
支持(0) 反对(0) czd890 | 园豆:14488 (专家六级) | 2023-09-26 17:43
0

示例

以下示例创建一组任务,用于对数组中的 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

收获园豆:20
echo_lovely | 园豆:1534 (小虾三级) | 2023-09-27 09:31

我感觉这个WhenAll 是关注于 任务执行情况的,比如出错、取消、完成等状态

支持(0) 反对(0) echo_lovely | 园豆:1534 (小虾三级) | 2023-09-27 09:34
0

好处是方便批量启动任务和批量等待任务完成。每个Task都直接await会导致任务被串行化,前一个完成后一个才启动,如果后续任务需要前置任务的结果才能开始当然没问题。如果多个任务没有依赖关系,批量启动时就不能await。需要先用变量保存Task,全部启动后再await变量,变量太多的话代码非常难看(var t1 = Async1(); var t2 = Async2(); var t3 = Async3(); ……; await t1; await t2; await t3; ……;),如果任务是在循环里启动的甚至不知道应该准备多少变量,这时使用Task.WhenAll就非常方便了。

收获园豆:20
coredx | 园豆:683 (小虾三级) | 2023-10-19 09:55
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册