我正在试着抛出并接住一个AggregateException。我在C#上没有太多使用异常,但我发现的行为有点令人惊讶。
我的代码是:
var numbers = Enumerable.Range(0, 20);
try
{
var parallelResult = numbers.AsParallel()
.Where(i => IsEven(i));
parallelResult.ForAll(e => Console.WriteLine(e));
}
catch (AggregateException e)
{
Console.WriteLine("There was {0} exceptions", e.InnerExceptions.Count());
}它正在调用函数IsEven
private static bool IsEven(int i)
{
if (i % 10 == 0)
throw new AggregateException("i");
return i % 2 == 0;
}这就抛出了AggregateException。
我预计代码会写出0,20范围内的每个偶数,并且“有1个异常”两次。
我得到的是打印的一些数字(它们是ForAll的随机原因),然后异常被抛出,但没有被捕获,程序停止。
我是不是遗漏了什么?
发布于 2013-10-16 05:20:00
这实际上是一种有趣的。我认为问题在于你以一种意想不到的方式使用了AggregateException,这导致了PLINQ代码内部的错误。
AggregateException的全部意义在于将可能在并行进程中同时(或几乎同时)发生的多个异常组合在一起。所以AggregateException应该至少有一个内部异常。但是您抛出的是new AggregateException("i"),它没有内部异常。PLINQ代码试图检查InnerExceptions属性,遇到某种错误(可能是NullPointerException),然后似乎进入了某种循环。这可以说是PLINQ中的一个bug,因为您使用的是AggregateException的有效构造函数,即使它是一个不寻常的构造函数。
正如其他地方指出的,抛出ArgumentException在语义上更正确。但是您可以通过抛出一个构造正确的AggregateException来获得您正在寻找的行为,例如,通过将IsEven函数更改为如下所示:
private static bool IsEven(int i)
{
if (i % 10 == 0){
//This is still weird
//You shouldn't do this. Just throw the ArgumentException.
throw new AggregateException(new ArgumentException("I hate multiples of 10"));
}
return i % 2 == 0;
}我认为这个故事的寓意是,除非你真正知道自己在做什么,否则不要抛出AggregateException,特别是当你已经在某种并行或Task-based操作中时。
发布于 2013-10-16 07:03:42
我同意其他人的观点:这是.Net中的一个错误,你应该使用report it。
原因在于内部类QueryTaskGroupState中的QueryEnd()方法。它的反编译代码(为清晰起见稍作修改)如下所示:
try
{
this.m_rootTask.Wait();
}
catch (AggregateException ex)
{
AggregateException aggregateException = ex.Flatten();
bool cacellation = true;
for (int i = 0; i < aggregateException.InnerExceptions.Count; ++i)
{
var canceledException =
aggregateException.InnerExceptions[i] as OperationCanceledException;
if (IsCancellation(canceledException))
{
cacellation = false;
break;
}
}
if (!cacellation)
throw aggregateException;
}
finally
{
this.m_rootTask.Dispose();
}
if (!this.m_cancellationState.MergedCancellationToken.IsCancellationRequested)
return;
if (!this.m_cancellationState.TopLevelDisposedFlag.Value)
CancellationState.ThrowWithStandardMessageIfCanceled(
this.m_cancellationState.ExternalCancellationToken);
if (!userInitiatedDispose)
throw new ObjectDisposedException(
"enumerator", "The query enumerator has been disposed.");基本上,这样做的目的是:
如果扁平化的AggregateException包含任何非取消,则
ObjectDisposedException (假设userInitiatedDispose为false,它是false)<代码>F215
因此,如果抛出一个没有内部异常的AggregateException,ex将是一个包含空AggregateExcaption的AggregateException。调用Flatten()将把它变成一个空的AggreateException,这意味着它不包含任何非取消异常,所以代码的第一部分认为这是取消,不会抛出异常。
但是代码的第二部分意识到这不是取消,所以它抛出了一个完全虚假的异常。
https://stackoverflow.com/questions/19266338
复制相似问题