316

I am porting some code to Parallel.ForEach and got an error with a continue I have in the code. Is there something equivalent I can use in a Parallel.ForEach functionally equivalent to continue in a foreach loop?

Parallel.ForEach(items, parallelOptions, item =>
{
    if (!isTrue)
        continue;
});
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
John Egbert
  • 5,496
  • 8
  • 32
  • 44
  • 2
    As a side note, if the intention of the `isTrue` variable is to terminate the loop while it's running, the correct way to do it is to create a `CancellationTokenSource` instance and pass its `Token` property to the `ParallelOptions.CancellationToken` property. See [here](https://stackoverflow.com/questions/70827715/stop-parallel-foreachasync/70827899#70827899 "Stop Parallel.ForEachAsync") for an example. The `CancellationTokenSource` class is thread-safe, and takes care of visibility issues that might catch you off-guard if you are not familiar with `volatile` fields and memory barriers. – Theodor Zoulias Jul 03 '23 at 02:18

3 Answers3

529
return;

(the body is just a function called for each item)

dave
  • 12,634
  • 3
  • 20
  • 12
40

When you converted your loop into a compatible definition for the Parallel.Foreach logic, you ended up making the statement body a lambda. Well, that is an action that gets called by the Parallel function.

So, replace continue with return, and break with Stop() or Break() statements.

Squazz
  • 3,912
  • 7
  • 38
  • 62
Taran
  • 2,895
  • 25
  • 22
  • 2
    A possible better option than replacing breaks with return statements is ParallelLoopState's Stop() and Break(). http://blogs.msdn.com/b/pfxteam/archive/2009/05/27/9645023.aspx – JasonCoder Jul 08 '16 at 21:31
  • 1
    @JasonCoder none of those are equivalent to `continue` though. – will Aug 25 '16 at 16:06
  • 1
    @will correct, which is why I said breaks. return statements do replace continue statements – JasonCoder Aug 25 '16 at 18:54
  • @JasonCoder - Ah. i misunderstood what you meant, whoops. – will Aug 26 '16 at 08:49
  • The page linked by @JasonCoder is now at https://devblogs.microsoft.com/pfxteam/exiting-from-parallel-loops-early/ – Ian Kemp May 25 '22 at 12:12
-3

Simply refactor your code, there is little reason to use a continue in high level languages.

Your example would indicate something like:

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
});

The danger is when the code above your continue gets larger and you miss the continue when you come back in 6 months to make some changes. (I also always put in braces around any if output to protect against additions inside the if).

How I would do it.

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
    }
});

So the code above doesn't use or save any more system resources but it does mean if you are quickly scanning the code you will immediately see that the code only done to some elements is obviously inside a conditional due to the tabbed in nature of it.

If you are not putting any code above the continue then it is pointless (and a waste of cpu cycles & RAM) to have it in there, in that case I would refactor to something like the following:

Parallel.ForEach(items.Where(x => x.IsTrue()), parallelOptions, item =>
{
    //Code that is only done to elements passing a certain test
});

Now in the above case you may not want to use a static, modern C# should be able to call a static extension from a different thread to main, it just means instead of storing a new copy of the function on every object, you store it once and the process is cached in every thread that is opened by your program.

Instead you could be looking at a Bool on the object, or if you want to make absolutely sure the check on whether to include this item in the processing is done or not, then the second last example is exactly what you would use.

Slipoch
  • 750
  • 10
  • 23
  • 1
    What does "there is little reason to use a continue in high level languages" mean? – Enigmativity Jul 03 '23 at 02:04
  • 1
    And by doing `items.Where(x => x.IsTrue())` you have managed to change a task that is running in parallel to one that is partially not running in series. I know the OP hasn't explained how `isTrue` is computed, but by changing it to `x.IsTrue()` you've introduced some sort of computation that may be the expensive part that the OP is trying to avoid. – Enigmativity Jul 03 '23 at 02:06
  • 2
    "What does "static functions can be run from asynchronous threads and the JIT will usually run it on the same thread" mean? Threads aren't asynchronous. Are you suggesting that the JIT writes code that synchronizes something (I don't know what the "it" is you speak of)? – Enigmativity Jul 03 '23 at 02:23
  • @Enigmativity as higher level languages like C# include things like foreach loops, where, and more advanced elements it makes little sense to use the anti-pattern of continue, especially when you can make it obvious what is going on in the loops control. I have been updating legacy codebases for many years and one of the things that always causes issues is the use of continue (not so much break) where it is not needed as when surrounded by code that has been added over the years it then means you have to go into every loop and analyse each line when looking for bugs. – Slipoch Jul 03 '23 at 02:25
  • Slipoch FYI an answer similar to your was posted 3 years ago, and deleted a few months later ([screenshot](https://prnt.sc/bH-nilQWmWgu)). – Theodor Zoulias Jul 03 '23 at 02:35
  • @Enigmativity sorry I was meaning multi-thread programming, as I think about it as concurrency I used async technically incorrectly. If I call an global static (it) from a separate thread the compiler that runs as the program runs should automatically check if the static is touching anything that exists on the main thread, my understanding is that it is cached in the program and copied to each thread (otherwise you use threadstatic) it should then run the same transforms on the data whilst on a different thread without touching the main thread. – Slipoch Jul 03 '23 at 02:41
  • @TheodorZoulias is that because the continue/break crowd downvoted it into oblivion? I also gave the reasoning why you would do it this way, several different ways of refactoring it and my own experience having to fix code that has used the continue. – Slipoch Jul 03 '23 at 02:51
  • @TheodorZoulias I like your comment about the cancellationtoken, that is a really important one to note. – Slipoch Jul 03 '23 at 03:00
  • TO make it clear, you do not HAVE to run the last example here. I gave 3 examples of refactoring that removed the need for a continue at all. In the last example I give the example of using a static extension, these are copied to each thread of the program that is created, independant of individual objects and perform the same transform of the data from each thread, you could use this inside the foreach area or in the conditional if it is not expensive and you are running the parrallel statement from the main thread. Or you could use a variable or any of the other possibilities. – Slipoch Jul 03 '23 at 03:06
  • "the compiler that runs as the program runs should automatically check if the static is touching anything that exists on the main thread" - no, not true. The compiler doesn't do anything like that. – Enigmativity Jul 03 '23 at 03:11
  • @Enigmativity global statics are copied to every thread the program makes, if a global static references something on the main thread then they run on the thread they are called from. I have never seen a global static touch anything on the main thread but I know the JIT compilation in C# does similar things when deciding whether to run an async/await on a different thread or keep it on the main thread. As while the non-blocking is the major reason for the async/await it does shift them between threads as it sees fit. – Slipoch Jul 03 '23 at 03:13
  • "...An await is the location in the code where the code needs the results from the asynchronous method to move on because downstream code needs the results. Well, the main thread has noting to do when it gets to the last await and it returns to the thread pool (Core) for other processes to use. Once the asynchronous method finishes then another thread takes over to run any remaining code. " - MS – Slipoch Jul 03 '23 at 03:19
  • 1
    There's no such things as `globals` in c#. Do you just mean a `static class`? I really don't know what you mean by "global static touch anything on the main thread"? And, no, "I know the JIT compilation in C# does similar things when deciding whether to run an async/await on a different thread or keep it on the main thread" - this doesn't happen. The threads are always run on the calling thread unless you explicitly call a `Task.Run`. – Enigmativity Jul 03 '23 at 03:21
  • Besides this is all beside the point, the point was instead of using continue you can simply refactor. The precise method of refactoring is up to them, otherwise if you still wish to use an antipattern then return answer or non-antipattern: the cancellation token comment are the best answers IMO. – Slipoch Jul 03 '23 at 03:22
  • 1
    @TheodorZoulias has an interesting solution, but it's not clear from the OP's question if they are just trying to stop processing the current element or the entire computation. – Enigmativity Jul 03 '23 at 03:24
  • @Enigmativity depending on the Net framework, yes it does (at least according to MS and according to the thread count in-program). You cannot be doing any CPU-bound calls and there are various other conditions and it doesn't 'start' on another thread but only assigns the remaining code to another thread from the pool once the await returns a result. So it is considerably more limited. Sorry global static is something from the C++ days. – Slipoch Jul 03 '23 at 03:29
  • @Slipoch - I think your understanding of how `async`/`await` works is a little off. – Enigmativity Jul 03 '23 at 03:54
  • Slipoch I think that choosing between `return`/`continue` and `if`, is a code-style discussion. This question is much more specific. It's about modifying existing code from `foreach` to `Parallel.ForEach`, so that the code compiles and the existing behavior is preserved. The accepted answer by Dave tackles successfully the question, and there is not much to be added. If you want to share your opinion about the coding style, [here](https://stackoverflow.com/questions/9267643/if-return-vs-if-else-efficiency "if->return vs. if->else efficiency") is a question that might be a better match. – Theodor Zoulias Jul 03 '23 at 04:02
  • This question might be even more relevant: [Is returning early from a function more elegant than an if statement?](https://stackoverflow.com/questions/355670/is-returning-early-from-a-function-more-elegant-than-an-if-statement) – Theodor Zoulias Jul 04 '23 at 14:59