C# Task 使用 WhenAll 和 WaitAll 需要注意的坑

2022-05-07 19:07:20 浏览数 (2)

1.无限等待

我们在使用 WhenAll 和 WaitAll 时,一定得要注意:

1.必须添加超时时间,防止无限等待

2.等待的 Task 一定要保证是启动的。

比如下面这种写法:

代码语言:javascript复制
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TaskForWhenAll
{
    class Program
    {
        static void Main(string[] args)
        {
            var taskList = new List<Task>();
            for (int i = 0; i < 5; i  )
            {
                taskList.Add(new Task(() =>
                {
                    Console.WriteLine("Task {0} is finished", Task.CurrentId);
                }));
            }

            // await Task.WhenAny(taskList);
            Task.WaitAll(taskList.ToArray());
            Console.WriteLine("exit");
        }
    }
}

将不会正常运行,会一直无限等待,因为 new Task 这样创建出来的 Task 不会自动运行,需要手动调用 Task.Start

改造一下代码:

代码语言:javascript复制
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace TaskForWhenAll
{
    class Program
    {
        static void Main(string[] args)
        {
            var taskList = new List<Task>();
            for (int i = 0; i < 5; i  )
            {
                taskList.Add(Task.Run(() =>
                {
                    Console.WriteLine("Task {0} is finished", Task.CurrentId);
                }));
            }

            // await Task.WhenAny(taskList);
            Task.WaitAll(taskList.ToArray());
            Console.WriteLine("exit");
        }
    }
}

使用 Task.Run 创建的 Task 是会自动运行的

2.没有期望的等待

我们在创建 Task 时,可能会定义一个异步委托,以便方便在 Task 里面使用 await,例如:

代码语言:javascript复制
class Program
{
    static async Task Main(string[] args)
    {
        var taskList = new List<Task>();
        for (int i = 0; i < 5; i  )
        {
            taskList.Add(Task.Factory.StartNew(async () =>
            {
                await Task.Delay(3000);
                Console.WriteLine("Task {0} is finished", Task.CurrentId);
            }));
        }

        await Task.WhenAny(taskList);
        Console.WriteLine("exit");
    }
}

直接运行会发生什么

直接就退出了,并没有等待所有任务执行完毕。

我们换成 Task.Run 是可以正常运行的,这是为什么呢?这是因为我们这种写法,会把 Task Result 包装一层,我们需要得到期望的结果需要解除包装 UnWrap,Task.Factory.StartNew和Task.Run区别之一就有Task.Run会自动执行Unwrap操作。

我们改造一下代码

代码语言:javascript复制
taskList.Add(Task.Factory.StartNew(async () =>
                {
                    await Task.Delay(3000);
                    Console.WriteLine("Task {0} is finished", Task.CurrentId);
                }).Unwrap());

就能按照我们期望的去运行

0 人点赞