使用任务和 Task.WaitAny 进行异常处理

Exception handling using tasks and Task.WaitAny

Task.WaitAll 方法可以抛出 AggregateException,但 Task.WaitAny 方法不会。

问题:

  1. 我不明白框架的开发者这样做的目的是什么?
  2. 如何使用 Task.WaitAny 方法从任务中捕获异常?

Example:

using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            void MyMethodForDivideByZeroException()
            {
                int a = 1;
                a = a / 0;
            }

            void MyMethodForIndexOutOfRangeException()
            {
                int[] MyArrayForInt = new int[2] { 1, 2 };
                Console.WriteLine(MyArrayForInt[2]);
            }

            Task MyTask22 = new Task(MyMethodForDivideByZeroException);
            Task MyTask23 = new Task(MyMethodForIndexOutOfRangeException);

            MyTask22.Start();
            MyTask23.Start();

            Task.WaitAny(MyTask22, MyTask23); //No exceptions
            //Task.WaitAll(MyTask22, MyTask23); //AggregateException

            Console.WriteLine(MyTask22.Status);
            Console.WriteLine(MyTask23.Status);
        }
    }
}

WaitAny() 等待 任何 一项任务完成(成功或失败)。
当一个人这样做时,除了异常,异常不会传播(抛出)。

WaitAll() 等待 所有 任务完成。
由于它们都抛出异常,因此它们被聚合成一个 AggregateException.

您可以像这样检查错误:

var tasks = new[] { MyTask22, MyTask23 };
int taskIndex = Task.WaitAny(tasks); //No exceptions
        
if (taskIndex >= 0)
{
    throw tasks[taskIndex].Exception;
}

这里有更多有用的信息:

Task.WaitAny 方法的工作是通知您任务已完成,而不是任务已完成 成功

我会倒序回答你的问题:

  1. How to catch an exception from a task using Task.WaitAny method?

您可能会问如何抛出 已完成但失败的任务的异常。最简单的方法大概就是Wait任务:

var tasks = new Task[] { MyTask22, MyTask23 };
int completedTaskIndex = Task.WaitAny(tasks);
tasks[completedTaskIndex].Wait();
  1. I do not understand for what purpose the developers of the framework did this?

否则该方法将无法完成其预期的工作。您将无法使用此方法来获知任务已完成。您将得到一个无用的 expensive 异常,而不是获得已完成任务的索引。您可以捕获此异常,但您仍然不知道是哪个任务导致了异常。