如何从 Parallel.ForEach 循环中抛出异常?
How do I throw an Exception from inside a Parallel.ForEach loop?
我有一个 Parallel.Foreach
循环,可以像这样下载文件:
try
{
var parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 8;
int failedFiles = 0;
Parallel.ForEach(FilesToDownload, parallelOptions, tile =>
{
bool downloaded = DownloadTile(File);
if (downloaded)
{
//Downloaded :)
}
else
{
failedFiles++;
if (failedFiles > 10)
{
throw new Exception("10 Files Failed to download. Failing download");
}
}
parallelOptions.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (Exception ex)
{
throw; //This throws it up to a main method that cancels my program
}
我想知道从 Parallel.Foreach
方法内部抛出 Exception
的正确方法是什么?在我的实例中,我认为在抛出第一个异常后我将看到抛出 8 次异常。
在 Parallel.ForEach
循环中抛出异常的正确方法是什么?
您可以使用 CancellationToken.Register. Also have a look at this example.
您也可以停止 Parallel.Foreach
并抛出异常。
这个 example 演示了如何停止 For
循环;但是,您可以用相同的方式停止 ForEach
循环。请参阅下面的代码段。
private static void StopLoop()
{
double[] source = MakeDemoSource(1000, 1);
ConcurrentStack<double> results = new ConcurrentStack<double>();
// i is the iteration variable. loopState is a
// compiler-generated ParallelLoopState
Parallel.For(0, source.Length, (i, loopState) =>
{
// Take the first 100 values that are retrieved
// from anywhere in the source.
if (i < 100)
{
// Accessing shared object on each iteration
// is not efficient. See remarks.
double d = Compute(source[i]);
results.Push(d);
}
else
{
loopState.Stop();
return;
}
});
Console.WriteLine("Results contains {0} elements", results.Count());
}
如果需要,您可以使用 ConcurrentStack
相同的方式在循环停止后立即将异常收集到 return。
首先,最好使用 Interlocked.Increment(ref failedFiles)
而不是 failedFiles++
。否则,可能会发生 10-15 次失败,但由于缺少缓存同步和 compiler/jitter 优化的影响,您最终得到的计数器的值大约为 7-8。
你程序的循环可能会抛出更多的异常,但最后它会被聚合成一个 AggregateException
,而外部 catch()
将接收到那个单一的异常实例。如果你不想在 AggregateException
中出现更多异常,你可以使用 ==
而不是 >
if (Interlocked.Increment(ref failedFiles) == 10)
当循环内抛出异常时,Parallel.ForEach
阻止其他迭代开始,然后它等待当前 运行 迭代完成,然后它聚合它捕获的所有异常,打包它进入 AggregateException
并抛出该单个实例。这意味着在您的情况下,一个例外将阻止进一步下载。
我有一个 Parallel.Foreach
循环,可以像这样下载文件:
try
{
var parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 8;
int failedFiles = 0;
Parallel.ForEach(FilesToDownload, parallelOptions, tile =>
{
bool downloaded = DownloadTile(File);
if (downloaded)
{
//Downloaded :)
}
else
{
failedFiles++;
if (failedFiles > 10)
{
throw new Exception("10 Files Failed to download. Failing download");
}
}
parallelOptions.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (Exception ex)
{
throw; //This throws it up to a main method that cancels my program
}
我想知道从 Parallel.Foreach
方法内部抛出 Exception
的正确方法是什么?在我的实例中,我认为在抛出第一个异常后我将看到抛出 8 次异常。
在 Parallel.ForEach
循环中抛出异常的正确方法是什么?
您可以使用 CancellationToken.Register. Also have a look at this example.
您也可以停止 Parallel.Foreach
并抛出异常。
这个 example 演示了如何停止 For
循环;但是,您可以用相同的方式停止 ForEach
循环。请参阅下面的代码段。
private static void StopLoop()
{
double[] source = MakeDemoSource(1000, 1);
ConcurrentStack<double> results = new ConcurrentStack<double>();
// i is the iteration variable. loopState is a
// compiler-generated ParallelLoopState
Parallel.For(0, source.Length, (i, loopState) =>
{
// Take the first 100 values that are retrieved
// from anywhere in the source.
if (i < 100)
{
// Accessing shared object on each iteration
// is not efficient. See remarks.
double d = Compute(source[i]);
results.Push(d);
}
else
{
loopState.Stop();
return;
}
});
Console.WriteLine("Results contains {0} elements", results.Count());
}
如果需要,您可以使用 ConcurrentStack
相同的方式在循环停止后立即将异常收集到 return。
首先,最好使用 Interlocked.Increment(ref failedFiles)
而不是 failedFiles++
。否则,可能会发生 10-15 次失败,但由于缺少缓存同步和 compiler/jitter 优化的影响,您最终得到的计数器的值大约为 7-8。
你程序的循环可能会抛出更多的异常,但最后它会被聚合成一个 AggregateException
,而外部 catch()
将接收到那个单一的异常实例。如果你不想在 AggregateException
中出现更多异常,你可以使用 ==
而不是 >
if (Interlocked.Increment(ref failedFiles) == 10)
当循环内抛出异常时,Parallel.ForEach
阻止其他迭代开始,然后它等待当前 运行 迭代完成,然后它聚合它捕获的所有异常,打包它进入 AggregateException
并抛出该单个实例。这意味着在您的情况下,一个例外将阻止进一步下载。