Home > Enterprise >  Parallel.ForEach: Break and ParallelLoopState.LowestBreakIteration. What to do about it?
Parallel.ForEach: Break and ParallelLoopState.LowestBreakIteration. What to do about it?

Time:12-22

In this article on docs microsoft, in the example for Parallel.For in the method, there is a Break call and processing of properties such as ShouldExitCurrentIteration and LowestBreakIteration in the following way:

    if (state.ShouldExitCurrentIteration)
    {
       if (state.LowestBreakIteration < i)
       return;
    }

LowestBreakIteration stores the number of the minimum iteration in which Break method was called. Also, this property can store the value of the internally-generated index for example in the case of the method Parallel.ForEach (source on docs microsoft)

Question. What should I do with property LowestBreakIteration in case of Parallel.ForEach, how should I use it and what should I compare with?

I easily repeated the example with Break for Parallel.For, but I can't figure out how to use the property LowestBreakIteration in the example with Break for Parallel.ForEach.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //Parallel.For
            void MyMethodForParallelForBreak(int i, ParallelLoopState MyParallelLoopState)
            {
                Console.WriteLine($"Start {i}");

                if (MyParallelLoopState.ShouldExitCurrentIteration)
                {
                    if (MyParallelLoopState.LowestBreakIteration < i) //Processing LowestBreakIteration here
                    {
                        Console.WriteLine($"Current {i}. Return: LowestBreakIteration {MyParallelLoopState.LowestBreakIteration} < {i}");
                        return;
                    }
                }

                if (MyParallelLoopState.LowestBreakIteration != null)
                {
                    Console.WriteLine($"Current {i}. LowestBreakIteration {MyParallelLoopState.LowestBreakIteration}");
                }

                if (i > 5)
                {
                    MyParallelLoopState.Break();

                    Console.WriteLine($"Current {i}. Break");
                }

                Console.WriteLine($"End {i}");
            }

            Parallel.For(1, 11, MyMethodForParallelForBreak);

            Console.WriteLine("\n");



            //Parallel.ForEach
            void MyMethodForParallelForEachBreak(KeyValuePair<string, string> MyKeyValuePair, ParallelLoopState MyParallelLoopState)
            {
                Console.WriteLine($"Start {MyKeyValuePair.Key}");

                if (MyParallelLoopState.ShouldExitCurrentIteration)
                {
                    if (MyParallelLoopState.LowestBreakIteration < ) //Error. Unknown. Processing LowestBreakIteration here
                    {
                        Console.WriteLine($"Current {MyKeyValuePair.Key}. Return: LowestBreakIteration {MyParallelLoopState.LowestBreakIteration} < Unknown");
                        return;
                    }
                }

                if (MyParallelLoopState.LowestBreakIteration != null)
                {
                    Console.WriteLine($"Current {MyKeyValuePair.Key}. LowestBreakIteration {MyParallelLoopState.LowestBreakIteration}");
                }

                if (MyKeyValuePair.Value == "a")
                {
                    MyParallelLoopState.Break();

                    Console.WriteLine($"Current {MyKeyValuePair.Key}. Break");
                }

                Console.WriteLine($"End {MyKeyValuePair.Key}");
            }


            Dictionary<string, string> MyDictionaryForStringAndString = new Dictionary<string, string>();

            MyDictionaryForStringAndString.Add("a1", "abc");
            MyDictionaryForStringAndString.Add("a2", "ab");
            MyDictionaryForStringAndString.Add("a3", "a");
            MyDictionaryForStringAndString.Add("a4", "abc");
            MyDictionaryForStringAndString.Add("a5", "ab");
            MyDictionaryForStringAndString.Add("a6", "a");
            MyDictionaryForStringAndString.Add("a7", "abc");
            MyDictionaryForStringAndString.Add("a8", "ab");
            MyDictionaryForStringAndString.Add("a9", "a");
            MyDictionaryForStringAndString.Add("a10", "abc");

            Parallel.ForEach(MyDictionaryForStringAndString, MyMethodForParallelForEachBreak);
        }
    }
}

CodePudding user response:

You can check ShouldExitCurrentIteration property to see if you need to exit the parallel foreach:

if (state.ShouldExitCurrentIteration) {
  // some other thread called state.Break()
  return;
}

CodePudding user response:

If you want to check LowestBreakIteration in a Parallel.Foreach loop there is an overload that takes a Action<TSource, ParallelLoopState, long> parameter. Where the long-value represents the index.

I would note that the usage of LowestBreakIteration / ShouldExitCurrentIteration seem very esoteric to me. The only use case I can imagine would be of each iteration involved multiple expensive operations, so you want to exit as fast as possible, and you still want to process all items before the iteration that called the break.

I for sure have never felt the need for such functionality, and while it is probably nice to have it for those who do need it, I would not worry about it.

  • Related