16 Task.WhenAll、Task.WhenAny用法

203 阅读2分钟

一、应用场景

1.Task.WhenAll,所有Task完成,Task才完成,用于等待多个任务执行结束,但是不在乎它们的执行顺序。

2.Task.WhenAny,任何一个Task完成,Task就完成。

准备一个文件夹TEMP,里面新建两个文本文件,并在文件中写入一些内容

image.png

其中1.txt中的文本长度为11,2.txt中文本内容长度为10

代码示例:获取文件夹中所有文件内容字符长度之和(Task.WhenAll)

    static async Task Main(string[] args)
    {
        string[] files = Directory.GetFiles(@"C:\TEMP");
        Task<int>[] countTasks = new Task<int>[files.Length];
        for (int i = 0; i < files.Length; i++)
        {
            string filename = files[i];
            Task<int> t = ReadCharsCount(filename);
            countTasks[i] = t;
        }
        int[] counts = await Task.WhenAll(countTasks);//Task<TResult[]>
        int c = counts.Sum();
        Console.WriteLine(c);
    }


    /// <summary>
    /// 获取文件中字符个数
    /// </summary>
    /// <param name="filename"></param>
    /// <returns></returns>
    static async Task<int> ReadCharsCount(string filename)
    {
        string s = await File.ReadAllTextAsync(filename);
        return s.Length;
    }

执行结果:

image.png

代码示例:获取文件夹中任一文件内容字符长度(Task.WhenAny)

    static async Task Main(string[] args)
    {
        string[] files = Directory.GetFiles(@"C:\TEMP");
        Task<int>[] countTasks = new Task<int>[files.Length];
        for (int i = 0; i < files.Length; i++)
        {
            string filename = files[i];
            Task<int> t = ReadCharsCount(filename);
            countTasks[i] = t;
        }
        Task<int> counts = await Task.WhenAny(countTasks);//Task<Task<TResult>>
        Console.WriteLine(counts.Result);
    }


    /// <summary>
    /// 获取文件中字符个数
    /// </summary>
    /// <param name="filename"></param>
    /// <returns></returns>
    static async Task<int> ReadCharsCount(string filename)
    {
        string s = await File.ReadAllTextAsync(filename);
        return s.Length;
    }

执行结果:

image.png

获取到了1.txt中的字符串长度。

Task.WhenAll、Task.WhenAny都有多个重载方法,可以根据实际需要情况调用不同的重载方法。

二、应用优点

在每个异步方法中可以调用一个或多个异步方法。那么如何进行编码呢?这就看这些异步方法之间是否存在相互依赖了。

  正常来说按照顺序调用:

        

static void Main(string[] args)
        {
            SeeThreadAndTask($"运行{nameof(Main)}");

            ManyAsyncFun();

            Console.ReadLine();
        }

        private static async void ManyAsyncFun()
        {

            var result1 = await GetStringAsync("张三");

            var result2 = await GetStringAsync("李四");

            Console.WriteLine($"第一个人是{result1},第二个人是{result2}");
        }

 

 

  使用await关键字调用每个异步方法。如果一个异步方法依赖另一个异步方法的话,那么这个await关键字就比较有效,但是如果第二个异步方法独立于第一个异步方法,这样可以不使用await关键字,这样的话整个ManyAsyncFun方法将会更快的返回结果。

  还一种情况,异步方法不依赖于其他异步方法,而且不使用await,而是把每个异步方法的返回结果赋值给Task比变量,这样会运行的更快。组合器可以帮助实现这一点,一个组合器可以接受多个同一类型的参数,并返回同一类型的值。如果任务返回相同的类型,那么该类型的数组也可用于接收await返回的结果。当只有等待所有任务都完成时才能继续完成其他的任务时,WhenAll方法就有实际用途,当调用的任务在等待完成时任何任务都能继续完成任务的时候就可以采用WhenAny方法,它可以使用任务的结果继续。

       

 static void Main(string[] args)
        {
            SeeThreadAndTask($"运行{nameof(Main)}");

            ManyAsyncFunWithWhenAll();

            Console.ReadLine();
        }

        private static async void ManyAsyncFunWithWhenAll()
        {
            Task<string> result1 =   GetStringAsync("张三");

            Task<string> result2 =   GetStringAsync("李四");

            await Task.WhenAll(result1, result2);

            Console.WriteLine($"第一个人是{result1.Result},第二个人是{result2.Result}");
        }

 

在使用await依次调用两个异步方法时,诊断会话6.646秒,采用 WhenAll 时,诊断会话话费3.912秒,可以看出速度明显提高了。

       

                 

————————————————

参考文献: blog.csdn.net/liangmengbk… www.cnblogs.com/hulizhong/p…