Parallel.Foreach 循环,与显式 throw 语句的行为不一致

Parallel.Foreach loop, inconsistent behavior with explicit throw statement

使用 Linqpad 创建了一个简单的程序,我在 Parallel Foreach 循环中显式抛出一个异常,理想情况下应作为 Aggregate Exception 在调用者中捕获,但是当我显式抛出异常时,它有时会随机跳过一些例外。我无法理解这种行为,任何可以解释的人:

void Main()
{
    try
    {
      var intList = new List<int> {1,2,3,4,5,6};

      Parallel.ForEach(intList, i => Test1(i));
    }
    catch (AggregateException aggregateException)
    {
        foreach (var ex in aggregateException.Flatten().InnerExceptions)
        {
            ex.Message.Dump();
        }
    }
}

public void Test1(int i)
{
    try
    {
        if (i % 2 != 0)
            throw new Exception($"{i} - Odd value exception");

    }
    catch(Exception ex)
    {
        ex.Message.Dump();
        throw;
    }
}

public void Test2(int i)
{
        if (i % 2 != 0)
            throw new Exception($"{i} - Odd value exception");
}

public void Test3(int i)
    {
        try
        {
            if (i % 2 != 0)
                throw new Exception($"{i} - Odd value exception");

        }
        catch(Exception ex)
        {
            ex.Message.Dump();
        }
    }

详情:

  1. 有两个版本的测试,一个带有显式 Try Catch,另一个没有
  2. 两者都有类似的不一致行为,以至于在 Test1 中,即使是本地 try catch 也不会打印值
  3. 可以有第三个版本Test3,它始终有效,因为没有明确地将异常抛出并行循环
  4. Dump 是一个 linqpad 打印调用,在 visual studio
  5. 上用 Console.WriteLine 替换它

有一个选项define here,它把所有的异常收集到一个ConcurrentQueue中,稍后作为聚合异常抛出,但是为什么现在的代码没有按预期工作,我不是很当然。在这种情况下,我们期望输出为:

1 - Odd value exception
3 - Odd value exception
5 - Odd value exception

但是其中一些是随机跳过的,在一个简单的程序中也是如此,在一个复杂的程序中有更多的遗漏,它做更多的工作

这完全是预期的行为。

参见docs

an unhandled exception causes the loop to terminate immediately

当您抛出异常时,将不会安排新的任务。

所以行为会显得不可预测。您无权期望所有子任务都会执行。那不是 Parallel.For 循环的约定。

当您向源列表中添加更多项目时,差异会更加明显。 输出将始终显示 ThreadPool.MinThreads 附近的一些异常。