等待所有或任意异步任务完成,以及异步任务完成时的处理方案

本篇体验如何等待所有异步任务完成、等待任意一个异步任务完成,以及异步任务完成时的处理。

等待一组任务的完成使用Task.WhenAll方法。

Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
Task task3 = Task.Delay(TimeSpan.FromSeconds(3));

await Task.WhenAll(task1, task2, task3);

如果所有任务的结果类型相同且全部完成,Task.WhenAll返回每个任务执行结果的数组。

Task task1 = Task.FromResult(1);
Task task2 = Task.FromResult(2);
Task task3 = Task.FromResult(3);
int[] results = await Task.WhenAll(task1, task2, task3);
foreach(var item in results)
{
    Console.WriteLine(item);
}

举个例子,提供一个url的集合,要求根据这个url集合去远程下载对应的内容,写一个方法。

static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
    var httpClient = new HttpClient();

    //定义每一个ulr的使用方法
    var downloads = urls.Select(url => httpClient.GetStringAsync(url));

    //下载真正开始
    Task<string>[] downloadTasks = downloads.ToArray();

    //异步等待
    string[] hmtls = await Task.WhenAll(downloadTasks);

    return string.Concat(htmls);
}

如果在等待所有任务完成的过程中有异常发生怎么办呢?

如果想在等待过程中捕获异常,那么应该把WhenAll方法放在try语句块中;如果想在所有任务完成后捕获异常,那就应该把WhenAll方法返回的Task类型放在try语句块中。

先模拟两个异步异常。

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

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

首先来看等待结果出来时的异常处理。

stati async Task ObserveOneExceptionAsync()
{
    var task1 = ThrowNotImplementedExceptionAsync();
    var task2 = ThrwoInvalidOperationExceptionAsync();

    try
    {
        await Task.WhenAll(task1, ask2);
    }
    cach(Exception ex)
    {

    }
}

再来看等所有结果出来后的异常处理。

static async Task ObserveAllExceptionAsync()
{
    var task1 = ThrowNotImplementedExceptionAsync();
    var task2 = ThrwoInvalidOperationExceptionAsync();

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

    try
    {
        await allTasks;
    }
    catch(Eexception ex)
    {

    }
}

等待任意一个任务的完成使用WhenAny方法。

比如有2个任务,通过2个url获取异步远程内容。

private static async Task<int> DownloadAsync(string url1, url2)
{
    var httpClient = new HttpClient();

    Task<byte[]> download1 = httpClient.GetByteArrayAsync(url1);
    Task<byte[]> download2 = httpClient.GetByteArrayAsync(url2);

    //等待任意一个任务完成
    Task<byte[]> completedTask = await Task.WhenAny(download1, download2);

    byte[] data = await completedTask;
    return data.Length;
}

任务完成时如何处理呢?

思路有2个,一个是根据我们安排的顺序出结果,还有一个是根据任务本身出结果的先后顺序自然输出结果。

首先来一个异步方法。

static async Task<int> DelayAsync(int val)
{
    await Task.Delay(TimeSpan.FromSeconds(val));
    return val;
}

再写一个手动部署任务顺序的方法。

static async Task ProcessTasksAsync()
{
    //创建任务队列
    Task<int> task1 = DelayAsync(2);
    Task<int> task2 = DelayAsync(3);
    Task<int> task3 = DelayAsync(1);

    //手动安排任务的顺序
    var tasks = new[]{task1, task2, task3};

    //按顺序遍历任务列表,逐一输出结果
    foreach(var task in tasks)
    {
        var result = await task;
        Console.Write(result);
    }
}

输出结果为231,是根据我们手动安排任务的顺序输出结果的。

如果我们想输出123呢?即按照任务的不同让结果自然发生。

思路是:以异步的方式处理输出结果。

可以写一个针对每个任务的异步方法。

static async Task AwaitAndProessAync(Task<int> task)
{
    var result = await task;
    Console.Write(result);
}

现在修改ProcessTasksAsync方法如下:

static async Task ProcessTasksAsync()
{
    //创建任务队列
    Task<int> task1 = DelayAsync(2);
    Task<int> task2 = DelayAsync(3);
    Task<int> task3 = DelayAsync(1);

    //手动安排任务的顺序
    var tasks = new[]{task1, task2, task3};

    var processingTasks = (from t in tasks
                        select AwaitAndProessAync(t)).ToArray();

     await Task.WhenAll(processingTasks);
}

当然,也可以这样修改ProcessTasksAsync方法。

static async Task ProcessTasksAsync()
{
    //创建任务队列
    Task<int> task1 = DelayAsync(2);
    Task<int> task2 = DelayAsync(3);
    Task<int> task3 = DelayAsync(1);

    //手动安排任务的顺序
    var tasks = new[]{task1, task2, task3};

    var processingTasks = tasks.Select( async t => {
        var result = await t;
        Console.Write(result);
    }).ToArray();


     await Task.WhenAll(processingTasks);
}


参考资料:C#并发编程经典实例

时间: 2024-12-20 00:32:26

等待所有或任意异步任务完成,以及异步任务完成时的处理方案的相关文章

咱们来聊聊JS中的异步,以及如何异步,菜鸟版

为什么需要异步?why?来看一段代码. 问题1: for(var i=0;i<100000;i++){ } alert('hello world!!!'); 这段代码的意思是执行100...次后再执行alert,这样带来的问题是,严重堵塞了后面代码的执行,至于为什么,主要是因为JS是单线程的. 问题2: 我们通常要解决这样一个问题,如果我们需要在head里面加入script代码的话,一般会将代码写在window.onload里面(如果操作了dom的话),你有没有想过,为什么要加window.on

ajax中的async属性值之同步和异步及同步和异步区别

在Jquery中ajax方法中async用于控制同步和异步,当async值为true时是异步请求,当async值为fase时是同步请求.ajax中async这个属性,用于控制请求数据的方式,默认是true,即默认以异步的方式请求数据. jquery中ajax方法有个属性async用于控制同步和异步,默认是true,即ajax请求默认是异步请求,有时项目中会用到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出现假死状态,当这个AJAX执行完毕后才

C# 异步编程2 EAP 异步程序开发

在前面一篇博文记录了C# APM异步编程的知识,今天再来分享一下EAP(基于事件的异步编程模式)异步编程的知识.后面会继续奉上TPL任务并行库的知识,喜欢的朋友请持续关注哦. EAP异步编程算是C#对APM的一种补充,让异步编程拥有了一系列状态事件.如果你看过本系列的前一篇文章<C# 异步编程1 APM 异步程序开发>,并假设你是微软C#语言开发组的一员,现在让你来设计基于事件的异步编程模式.那你是会利用之前的APM进行改造?还是进行再次创造呢?所以当你对相关dll进行反编译,会惊喜的发现EA

异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 threadpool:线程池使用,设置线程池,ManualResetEvent9 Task初步接触 10 task:waitall waitany continueWhenAny continueWhenAll  11并行运算Parallel 12 异常处理.线程取消.多线程的临时变量和lock13 A

.Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果

尝试创建基于MVVM三层架构的异步任务: 场景:View层触发ViewModel层的动作请求,ViewModel层异步的从Model层查询数据,当数据返回或者请求超时时正确更新ViewModel层数据并触发View层的UI更新. 要求:View层保持UI响应,ViewModel层实现有超时控制的异步调用并返回结果 --------------------------- 实现三个Worker,Worker1演示调用超时返回,Worker2演示无超时成功返回,Worker3演示同样演示无超时返回 设

阻塞IO,非阻塞IO,异步IO和非异步IO 的区别

最近在研究java IO.NIO.NIO2(或者称AIO)相关的东西,有些概念还是要明确下. 按照<Unix网络编程>的划分,IO模型可以分为:阻塞IO.非阻塞IO.IO复用.信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO. 如何区分呢?首先一个IO操作其实分成了两个步骤: 1.发起IO请求 2.实际的IO操作 阻塞和非阻塞IO:在于第一个步骤是否会会被阻塞,如果会则是阻塞IO,否则是非阻塞IO. 异步和非异步(同步)IO:在于第二个步骤是否会阻塞,如果实际的I

事件异步(EAP)使用事件异步处理一些耗时操作

比如需要下载一些比较大的文件,如果使用会UI卡顿,使用异步可以节省一些时间 下面是一些例子: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Demo { class Program { static void Main(string[] args) { System.Net.WebClient client = new System.Net.WebCl

全局异步和主线程异步区别、改变PlaceHolder颜色、解决键盘弹起挡住文本框问题

1.全局异步执行耗时任务 dispatch_async(dispatch_get_global_queue(0, 0), ^{ }); 2.主线程异步刷新UI dispatch_async(dispatch_get_main_queue(), ^{ }); 3.改变PlaceHolder的颜色 [username_text setValue:[UIColor colorWithRed:1 green:1 blue:1 alpha:0.5] forKeyPath:@"_placeholderLab

网络请求数据 get请求方式 &nbsp; post请求 协议异步连接服务器 block异步连接服务器

网络请求三部 创建一个请求(添加接口,对接口进行解码,) 设定请求方式(将接口转为NSURL,设置请求[请求地址, 缓存策略, 超时时间],设置请求方式) 连接服务器([同步连接,异步连接]代理连接,block连接) #import "MainViewController.h" @interface MainViewController () @property (retain, nonatomic) IBOutlet UIImageView *ImageWiew; //get请求方法