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


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

    等待一组任务的完成使用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#并发编程经典实例

  • 相关阅读:
    《编写可维护的JavaScript》之编程实践
    CSS基础知识之float
    tab.js分享及浏览器兼容性问题汇总
    CSS基础知识之position
    样式布局分享-基于frozen.js的移动OA
    DoNet开源项目-基于jQuery EasyUI的后台管理系统
    DoNet开源项目-基于Amaze UI的点餐系统
    DSOframer 微软官方API的查阅方法
    DSOframer 的简单介绍和资源整理
    2015-新的阻力,新的努力
  • 原文地址:https://www.cnblogs.com/darrenji/p/4714588.html
Copyright © 2020-2023  润新知