在并行计算中,Stop与Break的区别。

19

我很难理解loopState.Stop()loopState.Break()。我已经阅读了MSDN和几篇文章,但我仍然感到困惑。

我理解的是,每次迭代分区器都会为线程提供要处理的剩余索引,loopState.Stop()将停止所有线程,而loopState.Break()将停止当前线程。

但是,让我们考虑以下情况:

Parallel.For(0, 100, (i, loopState) =>
{
    if (i >= 10) 
        loopState.Break();
    Debug.Write(i);
});

对于这个循环,我得到了以下结果:

0 25 1 2 3 4 5 6 7 8 9 10 

我不知道为什么结果中有数字10和25。

有人可以帮忙吗?

附言:我的CPU是i5 520M(2个核心 => 4个线程)。

7个回答

15

loopState.Break()并不像return那样中断函数,所以在loopState.Break()后面的代码行仍将被执行。对于该数字的范围,当作用域结束后,for会检查是否已调用了loopState.Break(),如果是,则允许所有循环继续直到达到调用Break的数字。

在您的示例中,从0到24的循环将与从25到49的循环同时中断(并显示它们的“中断”数字)。

由于第二个循环从25到49的起始数字大于中断数字10,因此50..74和75..99的循环甚至不会开始,因为第二个循环已经中止了整个for操作。


loopState.Break()会退出所有线程吗?我曾经认为是loopState.Stop()。 - Michał Jankowski
澄清一下我的先前评论:我现在明白了前两个循环如何停止,但为什么另外两个线程甚至没有开始?Break能够终止多个线程吗?如果可以,它是如何做到的? - Michał Jankowski
1
请参考以下链接了解Stop和Break的区别:https://dev59.com/5moy5IYBdhLWcg3wCpkz - Martin Mulder
@MichałJankowski 另外两个线程之所以没有启动,可能是因为它们没有足够的时间。如果在 loopState.Break() 前加上 Thread.Sleep(1),有时它们就会开始执行。 - Matthew Watson
@svick+Michal:我调整了我的答案。我想让我的答案尽可能简单。但这太简单了。感谢更新。 - Martin Mulder
我终于搞定了 :) - 非常感谢你简短明了的回答。 - Michał Jankowski

5

最简单的答案:

stop和break都阻止新的迭代开始。两者都确保已经开始的迭代完成。

区别- stop会中止它所在的迭代,而break不会。


5

Break()的文档中:

可以使用Break来通知循环,当前迭代后不需要再运行其他迭代。例如,如果在并行迭代从0到1000的for循环的第100次迭代中调用Break,则仍应运行小于100的所有迭代,但101到1000的迭代是不必要的。

这意味着当前迭代仍将完成(因此会打印10)。 Break()也无法进行时间旅行,因此25将保持打印状态。 Break()的意思是不会启动超过10的新迭代。


4

if (i >= 10) loopState.Break();会继续当前迭代。所以10被打印出来。

然而,在调用loopState.Break()之后,(i >= 10)的迭代将不再开始。

但是为什么会打印25?下面的图片将解释原因。由于有4个线程,0-99将被分成4部分。

第一个线程有:0-24。
第二个线程有:25-49。
第三个线程有:50-74。
第四个线程有:75-99。

根据我的理解,每个线程将独立循环数字。根据this post所述,它说:

如果在调用Break时已经启动了其他迭代,则可能会运行其他迭代。

由于第二个线程几乎与第一个线程同时启动,因此打印了“0, 25”。然后,在第二个线程中循环到“25”时调用了“if (i >= 10) loopState.Break();”。
第三个和第四个线程中的循环在调用“Break()”之前没有启动,因此没有打印任何大于“10”的数字。
图片参考:http://www.albahari.com/threading/part5.aspx

2
所有来自静态Parallel类的方法都返回ParallelLoopResult对象。该对象有两个属性-IsCompletedLowestBreakIteration
当我们使用loopState.Break()时,LowestBreakIteration返回一个整数,表示从哪个最低迭代调用了Break语句。
当我们使用loopState.Stop()时,LowestBreakIteration返回null

0

Break 确保所有当前正在运行的迭代都将完成。

Stop 只是终止了一切。


2
这不是真的。Stop() 会导致不再执行新的迭代,但所有正在运行的迭代/线程仍然允许完成。如果您想在 Action 中提前退出,则需要测试 state.IsStopped。 有关示例,请参见 https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallelloopstate.stop - huha
1
这并不是真的:https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallelloopstate.stop?#remarks - mr5

0
void Log(string prefix, bool isBreak=false) 
{
    var msg = isBreak ? " Break" : "";
    Console.WriteLine($"{prefix} task: {Task.CurrentId.ToString().PadLeft(3,'0')} {msg}");

}
long lockFlag=0;
Parallel.For(0, 130, (i, loopState) =>
{
    if (i >= 10 && Interlocked.Read(ref lockFlag)==0)
    {
        lockFlag=Interlocked.Increment(ref lockFlag);
        loopState.Break();
        //Statement after break will still execute for current iteration
        Log(i.ToString().PadLeft(3,'0'),true);
    }
    else
    {
      Log(i.ToString().PadLeft(3,'0')); 
    }
});   

enter image description here

任务“8904”持续运行以完成小于25的所有迭代。显然,如果已经完成的迭代表示大于25的值,则无法回滚。

如果您确实希望尽快终止循环,并且不关心保证所有先前的迭代都已完成,则ParallelLoopState还有另一种方法称为Stop()。 Stop方法尝试尽快结束循环-一旦发出,没有循环任务将开始新的迭代


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