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();
}
}
详情:
- 有两个版本的测试,一个带有显式 Try Catch,另一个没有
- 两者都有类似的不一致行为,以至于在 Test1 中,即使是本地 try catch 也不会打印值
- 可以有第三个版本
Test3
,它始终有效,因为没有明确地将异常抛出并行循环
Dump
是一个 linqpad 打印调用,在 visual studio 上用 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 附近的一些异常。
使用 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();
}
}
详情:
- 有两个版本的测试,一个带有显式 Try Catch,另一个没有
- 两者都有类似的不一致行为,以至于在 Test1 中,即使是本地 try catch 也不会打印值
- 可以有第三个版本
Test3
,它始终有效,因为没有明确地将异常抛出并行循环 Dump
是一个 linqpad 打印调用,在 visual studio 上用
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 附近的一些异常。