132

How do I break out of an parallel.for loop?

I have a pretty complex statement which looks like the following:

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),
    new Action<ColorIndexHolder>((ColorIndexHolder Element) =>
    {
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I)
        {
            Found = true;
            break;
        }
    }));

Using parallel class, I can optimize this process by far. However; I cannot figure out how to break the parallel loop? The break; statement throws following syntax error:

No enclosing loops out of which to break or continue

Rasmus Søborg
  • 3,597
  • 4
  • 29
  • 46

5 Answers5

211

Use the ParallelLoopState.Break method:

 Parallel.ForEach(list,
    (i, state) =>
    {
       state.Break();
    });

Or in your case:

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),
    new Action<ColorIndexHolder, ParallelLoopState>((ColorIndexHolder Element, ParallelLoopState state) =>
    {
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I)
        {
            Found = true;
            state.Break();
        }
    }));
Tudor
  • 61,523
  • 12
  • 102
  • 142
  • 1
    Thinking of a sequential foreach loop it's guaranteed that the items before the item that for whatever reason caused the break are processed. What about a Parallel.ForEach where order of the items does not necessarily have to be the order in which they are processed? Is it guaranteed as well that all items in an IEnumerable<...> before the one that invokes state.Break() are being processed and those coming after it are not? Although the former could be achieved somehow, I don't see how the latter would be possible at all. – Hendrik Wiese Mar 14 '13 at 12:29
  • 5
    @Hendrik Wiese: Docs say: `Calling the Break method informs the for operation that iterations after the current one don't have to execute. However, all iterations before the current one will still have to be executed if they haven't already.` and `there is no guarantee that iterations after the current one will definitely not execute.` – Tudor Jun 27 '14 at 09:19
  • 2
    so then would `state.Stop()` be more appropriate to reliably achieve the expected results, as mentioned below by Mike Perrenoud and MBentley – xtreampb Sep 08 '16 at 22:21
  • Is there a more intuitive variable name than state? – Kellen Stuart Dec 07 '20 at 22:42
54

You do this by calling using the overload of Parallel.For or Parallel.ForEach which passes in a loop state, then calling ParallelLoopState.Break or ParallelLoopState.Stop. The main difference is in how quickly things break - with Break(), the loop will process all items with an earlier "index" than the current. With Stop(), it will exit as quickly as possible.

For details, see How to: Stop or Break from a Parallel.For Loop.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 3
    +1, it looks like a few of us here have the exact same answer :) -- oh and I got your back up there on that other comment man. – Mike Perrenoud Sep 24 '12 at 19:00
  • Thanks for this explanation. Do you know when either break or stop are called, is it the case that the currently executing iterations are completed or does it stop the iterations mid way through execution? – CeejeeB May 07 '13 at 07:33
  • 1
    @CeejeeB Currently executing operations complete. – Reed Copsey May 10 '13 at 15:09
18

LoopState is certainly a great answer. I found the previous answers had so much other stuff that it was hard to see the answer, so here is a simple case:

using System.Threading.Tasks;

Parallel.ForEach(SomeTable.Rows(), (row, loopState) =>
{
    if (row.Value == testValue)
    {
        loopState.Stop();  // Stop the ForEach!
    }       
    // else do some other stuff here.
});
MBentley
  • 995
  • 10
  • 16
12

What you should be using is Any, rather than a foreach loop:

bool Found = ColorIndex.AsEnumerable().AsParallel()
    .Any(Element => Element.StartIndex <= I 
      && Element.StartIndex + Element.Length >= I);

Any is smart enough to stop as soon as it knows that the result must be true.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • True. However, `Any` won't work if you have to perform some sort of other lookup or logic inside an iteration. – dst3p Oct 21 '22 at 17:50
  • @dst3p Why do you think it wouldn't work? Sure, if you're looking something up in some other data structure or doing other complex logic you need to be sure it's safe to do in a multithreaded context, but there are lots of ways to do both of those things that *are* safe. Why would you think you couldn't look anything up or do any logic here? – Servy Oct 21 '22 at 20:06
  • you’re right, I’ve been living in a Java world for too long – dst3p Oct 22 '22 at 21:05
5

Just use the loopState that can be provided.

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),  
    new Action<ColorIndexHolder>((Element, loopState) => { 
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I) { 
            loopState.Stop();
        }     
})); 

Look at this MSDN article for an example.

Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232