问题

执行几个任务,等待它们全部完成。

使用场景

  • 几个独立任务需要同时进行
  • UI界面加载多个模块,并发请求

解决方案

Task.WhenAll 传入若干任务,当所有任务完成时,返回一个完成的任务。

重载方法

  • Task WhenAll(IEnumerable<Task>)
  • Task WhenAll(params Task[])
  • Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>>)
  • Task<TResult[]> WhenAll<TResult>(params Task<TResult>[])

    举例:
            var task1 = Task.Delay(TimeSpan.FromSeconds(1));
var task2 = Task.Delay(TimeSpan.FromSeconds(2));
var task3 = Task.Delay(TimeSpan.FromSeconds(3)); await Task.WhenAll(task1, task2, task3);

当任务返回结果类型相同,所有任务完成返回的是,存着每个任务执行结果的数组。

            var task1 = Task.FromResult(1);
var task2 = Task.FromResult(2);
var task3 = Task.FromResult(3); int[] array = await Task.WhenAll(task1, task2, task3); //{1,2,3}

返回的数组中结果的顺序,并非可控,如上述例子中,只是结果为包含了1,2,3的数组,顺序是不定的。

书中不建议使用以 IEnumerable 类型作为参数的重载。文中没有介绍作者不建议的原因。我找到作者个人博客的一篇文中中提到如下文字(文章地址:https://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html

The IEnumerable<> overloads allow you to pass in a sequence of tasks, such as a LINQ expression. The sequence is immediately reified (i.e., copied to an array). For example, this allows you to pass the results of a Select expression directly to WhenAll. Personally, I usually prefer to explicitly reify the sequence by calling ToArray() so that it’s obvious that’s what’s happening, but some folks like the ability to pass the sequence directly in.

该段文字解释了作者更喜欢使用LINQ结合ToArray的方式使用异步,因为作者认为代码会更清晰。书中有例子,如下所示:

        static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
// 定义每一个 url 的使用方法。
var downloads = urls.Select(url => httpClient.GetStringAsync(url));
// 注意,到这里,序列还没有求值,所以所有任务都还没真正启动。
// 下面,所有的 URL 下载同步开始。
Task<string>[] downloadTasks = downloads.ToArray();
// 到这里,所有的任务已经开始执行了。
// 用异步方式等待所有下载完成。
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}

如果报错记得添加如下引用

using System.Linq;
using System.Net.Http;

返回的Task的状态

            var task1 = ......;
var task2 = ......;
var task3 = ......; Task allTasks = Task.WhenAll(task1, task2, task3);

以上述伪代码为例说明allTasks的状态

  • 当task1出现异常,异常会抛给allTasks,allTasks的状态同task1状态,也是Faulted。
  • 当task1被取消,allTasks的状态是Canceled
  • 当task1, task2, task3,不出现异常,也未被取消,allTasks的状态是RanToCompletion

Task.WhenAll的异常处理

上面提到了异常处理,当一个task异常,该异常会被allTasks接收,当多个task异常,这些异常也都会被allTasks接收。但是task1抛异常,task2也出异常,但是try catch 处理await Task.WhenAll(task1, task2);只能抓取其中某一个异常。如何获取所有异常呢?书中列举了两种处理方法,代码如下

抛出异常的方法

        static async Task ThrowNotImplementedExceptionAsync()
{
throw new NotImplementedException();
} static async Task ThrowInvalidOperationExceptionAsync()
{
throw new InvalidOperationException();
}

第一种处理方式,只能获取其中一个异常

        static async Task ObserveOneExceptionAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// ex 要么是 NotImplementedException,要么是 InvalidOperationException
//...
}
}

第二种处理方式,可以获取所有异常

        static async Task ObserveAllExceptionsAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
Task allTasks = Task.WhenAll(task1, task2);
try
{
await allTasks;
}
catch
{
AggregateException allExceptions = allTasks.Exception;
//...
}
}

两种方式的区别是,await调用Task.WhenAll返回的Task对象,即例子中的allTasks,代码 await allTasks;

作者在书中将对Task.WhenAll的异常处理放在了讨论中,并说明了自己的处理方式

使用 Task.WhenAll 时,我一般不会检查所有的异常。通常情况下,只处理第一个错误就足够了,没必要处理全部错误。

显然作者更中意第一种方式。那么你呢?

参考文章:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.task.whenall?view=netcore-2.2

《C#并发编程经典实例》学习笔记—2.4 等待一组任务完成的更多相关文章

  1. 《C#并发编程经典实例》笔记

    1.前言 2.开宗明义 3.开发原则和要点 (1)并发编程概述 (2)异步编程基础 (3)并行开发的基础 (4)测试技巧 (5)集合 (6)函数式OOP (7)同步 1.前言 最近趁着项目的一段平稳期 ...

  2. 《C#并发编程经典实例》学习笔记—2.7 避免上下文延续

    避免上下文延续 在默认情况下,一个 async 方法在被 await 调用后恢复运行时,会在原来的上下文中运行. 为了避免在上下文中恢复运行,可让 await 调用 ConfigureAwait 方法 ...

  3. 《C#并发编程经典实例》学习笔记—3.1 数据的并行处理

    问题 有一批数据,需要对每个元素进行相同的操作.该操作是计算密集型的,需要耗费一定的时间. 解决方案 常见的操作可以粗略分为 计算密集型操作 和 IO密集型操作.计算密集型操作主要是依赖于CPU计算, ...

  4. 《C#并发编程经典实例》学习笔记—2.3 报告任务

    问题 异步操作时,需要展示该操作的进度 解决方案 IProgress<T> Interface和Progress<T> Class 插一段话:读<C#并发编程经典实例&g ...

  5. 《C# 并发编程 · 经典实例》读书笔记

    前言 最近在看<C# 并发编程 · 经典实例>这本书,这不是一本理论书,反而这是一本主要讲述怎么样更好的使用好目前 C#.NET 为我们提供的这些 API 的一本书,书中绝大部分是一些实例 ...

  6. [书籍]用UWP复习《C#并发编程经典实例》

    1. 简介 C#并发编程经典实例 是一本关于使用C#进行并发编程的入门参考书,使用"问题-解决方案-讨论"的模式讲解了以下这些概念: 面向异步编程的async和await 使用TP ...

  7. 《C#并发编程经典实例》学习笔记-关于并发编程的几个误解

    误解一:并发就是多线程 实际上多线程只是并发编程的一种形式,在C#中还有很多更实用.更方便的并发编程技术,包括异步编程.并行编程.TPL 数据流.响应式编程等. 误解二:只有大型服务器程序才需要考虑并 ...

  8. 《C#并发编程经典实例》学习笔记-第一章并发编程概述

    并发编程的术语 并发 同时做多件事情 多线程 并发的一种形式,它采用多个线程来执行程序. 多线程是并发的一种形式,但不是唯一的形式. 并行处理 把正在执行的大量的任务分割成小块,分配给多个同时运行的线 ...

  9. 并发编程概述--C#并发编程经典实例

    优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版.NET 中的程序库和语 ...

  10. 《Java并发编程实战》学习笔记 线程安全、共享对象和组合对象

    Java Concurrency in Practice,一本完美的Java并发参考手册. 查看豆瓣读书 推荐:InfoQ迷你书<Java并发编程的艺术> 第一章 介绍 线程的优势:充分利 ...

随机推荐

  1. vue-cli跳转到新页面的顶部

    我这里有两种方法都是可以用的 1,利用vue-router的默认模式hash,可以记录上一页的位置,如果需要点话,如果没有记录,在进入新页面的时候是返回到新页面的最顶部的 scrollBehavior ...

  2. antd-mobile less文件用模块方式引入

      config-overrides.js文件修改::::: const { injectBabelPlugin, getLoader } = require('react-app-rewired') ...

  3. 关于vue的数据增删的一些细节

    第一种情况:在vue中使用的数据必须先在data中定义数据,不然报错: 第二种情况:访问对象中不存在的值,是可以得到undefined,但是不会报错 第三种:vue只会监听data已经定义的值,后续添 ...

  4. C# 对串口的操作

    初始化 串口 SerialPort sp = new SerialPort(); sp.PortName = BasicParameters.IniReadValue(strPath, "C ...

  5. ip锁死怎么设置ip地址

    单击电脑网络连接图标,打开网络和共享中心   点击本地连接   点击详细信息,即可看到IP地址.子网掩码.默认网关.DNS服务器信息   再点击本地连接状态下的属性   找到Internet 协议版本 ...

  6. Mybatis中的Caused by: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 找不到Mapper.xml文件的问题

    1 首先在配置mapper-locations的时候: classpath:  只在现有目录下寻找配置文件 classpath*: 在现有的项目目录下和依赖的jar包下寻找xml配置文件

  7. javaWeb+servlet+mysql实现简单的企业员工管理系统

    企业员工信息管理系统 一.源码描述       本程序为企业员工信息管理系统.是javaEE一个系统,主要实现登录功能和两个模块信息的增删改查.可以作为JAVAweb学习,也可在原有基础上进行深一步的 ...

  8. pyqt5之简单窗口的创建

    在学完tkinter后,发现tkinter在布局方面特别的不方便(Tkinter资料:http://effbot.org/tkinterbook/tkinter-index.htm),因此学习pyqt ...

  9. 手机touch事件及参数【转】(自己懒得写了,找了一篇摘过来)

    [html5构建触屏网站]之touch事件 前言 一个触屏网站到底和传统的pc端网站有什么区别呢,交互方式的改变首当其冲.例如我们常用的click事件,在触屏设备下是如此无力. 手机上的大部分交互都是 ...

  10. json格式 (JavaScipt Object Notation)

    json格式 json语法规则: 01.对象表现形式 key:value 键值对 02.如果有多个数据,之间使用逗号隔开 k1:v1,k2:v2 03.把对象写在大括号中 var student={a ...