在Parallel.ForEach中是否有与'continue'相等的功能?

318
我正在将一些代码转换为Parallel.ForEach,但在代码中使用了continue时出现了错误。在Parallel.ForEach中是否有类似的功能可以替代foreach循环中的continue
Parallel.ForEach(items, parallelOptions, item =>
{
    if (!isTrue)
        continue;
});

2
顺便提一下,如果isTrue变量的意图是在循环运行时终止循环,正确的做法是创建一个CancellationTokenSource实例,并将其Token属性传递给ParallelOptions.CancellationToken属性。请参阅此处以获取示例。CancellationTokenSource类是线程安全的,并且处理了可见性问题,如果您对volatile字段和内存屏障不熟悉,可能会让您措手不及。 - Theodor Zoulias
3个回答

532
return;

(body只是一个针对每个项目调用的函数)


40

当您将循环转换为Parallel.Foreach逻辑的兼容定义时,您最终使语句体成为一个lambda表达式。好吧,那是Parallel函数调用的动作。

因此,请将continue替换为return,将break替换为Stop()Break()语句。


2
一个比用return语句替换break更好的选择是ParallelLoopState的Stop()和Break()。http://blogs.msdn.com/b/pfxteam/archive/2009/05/27/9645023.aspx - JasonCoder
1
@JasonCoder,这些都不等同于continue - will
1
@will正确,这就是为什么我说“中断”。返回语句确实可以替代继续语句。 - JasonCoder
@JasonCoder - 啊,我误解了你的意思,糟糕。 - will
@JasonCoder链接的页面现在位于https://devblogs.microsoft.com/pfxteam/exiting-from-parallel-loops-early/。 - Ian Kemp

-3

只需重构您的代码,高级语言中很少有使用 continue 的理由。

您的示例可能会表明:

Parallel.ForEach(items, parallelOptions, item =>
{
    //Code that has to be processed for all entries

    if (!isTrue) {
        continue;
    }

    //Code that is only done to elements passing a certain test
});

危险在于当你回来6个月后进行一些更改时,代码上方的继续变得越来越大,而你错过了继续。 (我还总是在if输出周围加上大括号,以防止在if内部添加内容)。
我会怎么做。
Parallel.ForEach(items, parallelOptions, item =>
{
    //Code that has to be processed for all entries

    if (isTrue) {
        //Code that is only done to elements passing a certain test
    }
});

所以上面的代码不使用或保存任何额外的系统资源,但这意味着如果你快速浏览代码,你会立即看到只有对某些元素进行了条件判断的代码,这是因为它的缩进方式。
如果在continue之前没有放置任何代码,那么将其放在那里是没有意义的(而且浪费CPU周期和内存),在这种情况下,我会重构成以下代码:
Parallel.ForEach(items.Where(x => x.IsTrue()), parallelOptions, item =>
{
    //Code that is only done to elements passing a certain test
});

在上述情况下,您可能不希望使用静态方法。现代的C#应该能够从不同的线程调用静态扩展方法,这意味着您只需在每个对象上存储一次函数的副本,而进程将被缓存在程序打开的每个线程中。

相反,您可以考虑在对象上设置一个布尔值,或者如果您希望确保在处理过程中是否包含此项的检查已经完成或尚未完成,则最后一个示例正是您需要使用的。


1
在高级语言中,使用continue的理由很少。 - Enigmativity
1
通过使用items.Where(x => x.IsTrue()),你成功地将一个并行运行的任务部分转变为了串行运行的任务。我知道原帖中没有解释isTrue是如何计算的,但是通过将其改为x.IsTrue(),你引入了某种可能是原帖作者试图避免的昂贵计算。 - Enigmativity
2
“静态函数可以从异步线程中运行,并且JIT通常会在同一线程上运行它”这句话是什么意思?线程并不是异步的。你是在暗示JIT会编写代码来同步某些东西(我不知道你所说的“它”是什么)吗? - Enigmativity
1
@TheodorZoulias有一个有趣的解决方案,但从OP的问题中并不清楚他们是想停止处理当前元素还是整个计算过程。 - Enigmativity
1
这个问题可能更加相关:从函数中提前返回是否比使用if语句更优雅? - Theodor Zoulias
显示剩余14条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接