有没有办法突破PLINQ的SELECT块?根据SELECT块中的条件,我想打破循环。到目前为止,我看到了使用Cancellation令牌从循环外部中断PLINQ的各种示例。但是,我的问题是从循环内部打破。
这是我的代码:
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
// todo: Find a way to Break the Parallel loop.
}
return aResult;
});
如果没有可用的技术,也许我需要找到一种困难的方法,比如使用带有volatile关键字的布尔标志。我将在PLINQ Select块中设置带有lock语句的标志,然后下一次,它将读取该标志并确定它是否应该中断。但是,不确定这是否是一个很好的解决方案。此外,我非常不愿意在PLINQ中使用锁,这可能违背并行计算的目的。使用Lock,我可以强制执行同步,而不是从并行编程中获益。
所以更好的主意?
答案 0 :(得分:3)
我建议您使用Parallel.For
代替PLINQ
:
为什么不PLINQ?
即使PLINQ提供退出支持 在查询执行中,PLINQ退出之间的差异 机制和Parallel.ForEach是实质性的。退出PLINQ 查询,使用取消令牌提供查询,如here所述。 使用Parallel.ForEach,每次迭代都会轮询退出标志。在 PLINQ,取消令牌每隔一段时间轮询一次,所以你 不能假设取消的查询会很快停止。
取自When To Use Parallel.ForEach and When to Use PLINQ中的退出操作部分。
在上面的文章中,您可以找到有关如何使用Parallel.ForEach
或Parallel.For
Stop
Break
方法停止ParallelLoopState
/ Parallel.For
的详细说明那里和here:
在这种情况下,"打破"意味着完成所有线程上的所有迭代 在当前线程的当前迭代之前,和 然后退出循环。 "停止"意味着尽快停止所有迭代 方便。
使用var simulationResults = new ConcurrentBag<double>();
Parallel.For(0, 10000, (i, loopState) =>
{
//I assume this method is thread safe
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
//or loopState.Break();
loopState.Stop();
}
simulationResults.Add(aResult);
});
您的代码应该是这样的:
Parallel.For
使用PLINQ
代替PLINQ
的一个缺点是需要保留输入数据顺序时。
使用Parallel.For
,您可以使用AsOrdered
方法轻松完成,而{{1}}更难(但仍有可能,请参阅链接文章了解详情)。
但是你没有提到这个要求所以我在提供的代码中忽略了它。
答案 1 :(得分:1)
如果您希望在第一个不是数字时中断,请使用TakeWhile
。如果您只是想跳过它们,可以使用Where
。
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
return someMethod();
})
.TakeWhile(result => !double.IsNaN(result));