Task.WaitAll,如何找到导致AggregateException的任务

6

假设我有以下代码:

var tasks = BuildTaskList();
try
{
    Task.WaitAll(tasks.ToArray());
}
catch (AggregateException exception)
{

}

我如何知道exception.InnerExceptions中的哪个异常是由哪个任务引发的?

4个回答

9
你仍然有一个任务列表,每个任务都有一个异常属性。通过使用它,你可以确定哪些异常属于哪个任务。但是,如果可以的话,最好使用Task.WhenAll或TaskFactory.ContinueWhenAll而不是阻塞等待。

每一个 Task.Exception 不是都指向传递给 catch 的同一个 AggregateException exception 吗? - noseratio - open to work
3
不,每个“Task”都不知道其他任务的存在,它只会有自己的异常。 - Matt Smith
1
我有充分的理由等待所有任务完成。但我猜这些信息对于其他人来说也是有用的。但有点奇怪的是,异常本身无法确定哪个任务失败了,因为它可能是本来就失败的子任务。 - jgauffin
@jgauffin,很多时候你可以使用await WhenAll而不是WaitAll - Carlos Garcia

2
var throwers = tasks.Where(task => task.Exception != null);

有一些任务可能已经抛出异常,它们的异常被聚合成一个 AggregateException。据我所知。 - noseratio - open to work
2
@Noseratio 上面的 throwers 是所有抛出异常的任务序列。可能是全部,可能是没有,也可能是中间任意数量 - 但它们都有自己的异常。 - Matthew Watson

0

选项1(感谢@theodor-zoulias的评论):

您可以使用Exception.Data属性设置任务名称:

var task1 = Task.Factory.StartNew(() =>
{
    try
    {
        throw new Exception();
    }
    catch (Exception exception)
    {
        exception.Data["task"] = "task1";
        throw exception;
    }
});

var task2 = Task.Factory.StartNew(() =>
{
    try
    {
        throw new Exception();
    }
    catch (Exception exception)
    {
        exception.Data["task"] = "task2";
        throw exception;
    }
});

try
{
    Task.WaitAll(task1, task2);
}
catch (AggregateException ae)
{
    foreach (var exception in ae.InnerExceptions)
    {
        Console.WriteLine($"Exception was thrown by {exception.Data["task"]}");
    }
}

选项2:

如果您不介意失去异常程序集信息的名称,您可以根据任务名称设置抛出异常的Exception.Sourcedoc)属性,并在迭代时查看它:

var t1 = Task.Factory.StartNew(() => 
{
    throw new Exception() { Source = "t1" };
});
var t2 = Task.Factory.StartNew(() => 
{
    throw new Exception() { Source = "t2" };
});

try
{
    Task.WaitAll(t1, t2);
}
catch (AggregateException ae)
{
    foreach (var exception in ae.InnerExceptions)
    {
        Console.WriteLine($"Exception was thrown by {exception.Source}");
    }
}

将输出:

t1 抛出了异常

t2 抛出了异常


Exception.Source 属性旨在提供有关引发异常的程序集的信息。通过劫持此属性,您将丢失信息。对于用户定义的信息,最好使用 Exception.Data 属性。 - Theodor Zoulias
“Data” 是一个 “IDictionary”,所以我认为你更新的代码无法编译。 - Theodor Zoulias

0
        var t1 = Task.Factory.StartNew(() => Console.WriteLine("Task 1"));
        var t2 = Task.Factory.StartNew(() => Console.WriteLine("Task 2"));
        var t3 = Task.Factory.StartNew(() => { throw new InvalidOperationException(); });
        var t4 = Task.Factory.StartNew(() => Console.WriteLine("Task 4"));

        Task.Factory.ContinueWhenAll(new[] { t1, t2, t3, t4 }, tasks =>
            {
                foreach (var t in tasks)
                {
                    if (t.Status == TaskStatus.Faulted)
                    {
                        // this will run for t3
                        Console.WriteLine("This task has been faulted.");
                    }
                }
            });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接