突破PLINQ Select Block(C#)

时间:2018-01-23 20:03:05

标签: c# plinq

有没有办法突破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,我可以强制执行同步,而不是从并行编程中获益。

所以更好的主意?

2 个答案:

答案 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.ForEachParallel.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));