【问题标题】:Breaking out from PLINQ Select Block (C#)从 PLINQ 选择块 (C#) 中脱颖而出
【发布时间】:2018-01-23 20:03:05
【问题描述】:

有什么方法可以从 PLINQ 的 SELECT 块中分离出来?根据 SELECT 块中的条件,我想打破循环。到目前为止,我看到了各种使用取消令牌从循环外部中断 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,我可能会强制执行同步,而无法获得并行编程的好处。

所以有更好的主意吗?

【问题讨论】:

  • 可以在Select之后添加.TakeWhile(result => !double.IsNaN(result))

标签: c# plinq


【解决方案1】:

我建议你使用Parallel.For 而不是PLINQ

为什么不用 PLINQ?

即使 PLINQ 支持退出 在一个查询执行中,PLINQ 的 exit 之间的区别 机制和 Parallel.ForEach 很重要。退出 PLINQ 查询,为查询提供取消令牌,如here 所述。 使用 Parallel.ForEach,每次迭代都会轮询退出标志。在 PLINQ,取消令牌每隔一段时间就会被轮询一次,所以你 不能假设取消的查询会很快停止。

取自When To Use Parallel.ForEach and When to Use PLINQ中的Exiting from Operations部分。

在上面的文章中,您可以找到详细说明如何使用StopBreakParallelLoopState 方法停止Parallel.ForEach/Parallel.For,其中详细描述了那里和here

在这种情况下,“break”意味着完成所有线程上的所有迭代 在当前线程上的当前迭代之前,以及 然后退出循环。 “停止”表示立即停止所有迭代 方便。

使用Parallel.For 你的代码应该是这样的:

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,您可以使用AsOrdered 方法轻松完成,而使用Parallel.For 则要困难得多(但仍然可以,请参阅链接文章了解详细信息)。
但是你没有提到这个要求,所以我在提供的代码中忽略了它。

【讨论】:

    【解决方案2】:

    如果您想在第一个不是数字时中断,请使用TakeWhile。如果你只是想跳过它们,你可以使用Where

           var simulationResults =
                Enumerable.Range(0, 10000)
                    .AsParallel()
                    .WithCancellation(token ?? CancellationToken.None)
                    .Select(z =>
                    {
                        progressAction?.Invoke();
                        return someMethod();
                    })
                    .TakeWhile(result => !double.IsNaN(result));
    

    【讨论】:

    • 谢谢,没想到解决办法这么简单。
    猜你喜欢
    • 2011-01-30
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多