Export (0) Print
Expand All

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

The following example shows how to break (or Exit in Visual Basic) out of a For loop, and also how to stop a loop. In this context, "break" means complete all iterations on all threads that are prior to the current iteration on the current thread, and then exit the loop. "Stop" means to stop all iterations as soon as convenient.

This example demonstrates a For loop; however, you can stop or break from a ForEach loop in the same way. In a ForEach loop, an iteration index is generated internally for each element in each partition.


namespace StopOrBreak
{
    using System;
    using System.Collections.Concurrent;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    class Test
    {
        static void Main()
        {
            StopLoop();
            BreakAtThreshold();

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static void StopLoop()
        {
            Console.WriteLine("Stop loop...");
            double[] source = MakeDemoSource(1000, 1);
            ConcurrentStack<double> results = new ConcurrentStack<double>();

            // i is the iteration variable. loopState is a 
            // compiler-generated ParallelLoopState
            Parallel.For(0, source.Length, (i, loopState) =>
            {
                // Take the first 100 values that are retrieved
                // from anywhere in the source.
                if (i < 100)
                {
                    // Accessing shared object on each iteration
                    // is not efficient. See remarks.
                    double d = Compute(source[i]);
                    results.Push(d);
                }
                else
                {
                    loopState.Stop();
                    return;
                }

            } // Close lambda expression.
            ); // Close Parallel.For

            Console.WriteLine("Results contains {0} elements", results.Count());
        }


        static void BreakAtThreshold()
        {
            double[] source = MakeDemoSource(10000, 1.0002);
            ConcurrentStack<double> results = new ConcurrentStack<double>();

            // Store all values below a specified threshold.
            Parallel.For(0, source.Length, (i, loopState) =>
            {
                double d = Compute(source[i]);
                results.Push(d);
                if (d > .2)
                {
                    // Might be called more than once!
                    loopState.Break();
                    Console.WriteLine("Break called at iteration {0}. d = {1} ", i, d);
                    Thread.Sleep(1000);
                }
            });

            Console.WriteLine("results contains {0} elements", results.Count());
        }

        static double Compute(double d)
        {
            //Make the processor work just a little bit.
            return Math.Sqrt(d);
        }


        // Create a contrived array of monotonically increasing
        // values for demonstration purposes. 
        static double[] MakeDemoSource(int size, double valToFind)
        {
            double[] result = new double[size];
            double initialval = .01;
            for (int i = 0; i < size; i++)
            {
                initialval *= valToFind;
                result[i] = initialval;
            }

            return result;
        }
    }

}


In a Parallel.For or Parallel.ForEach loop, you cannot use the same break or Exit statement that is used in a sequential loop because those language constructs are valid for loops, and a parallel "loop" is actually a method, not a loop. Instead, you use either the Stop or Break methods. Some of the overloads of Parallel.For accept an Action<int, ParallelLoopState> (Action(Of Integer, ParallelLoopState) in Visual Basic) as an input parameter. The ParallelLoopState object is created behind the scenes by the runtime, and you can give it any name you like in the lambda expression.

In the example, the StopLoop() method requires only 100 values from the source sequence and it does not matter which elements were retrieved. In this case the Stop method is used, because it tells all iterations of the loop, including those begun prior to the current iteration on other threads, to stop as soon as is convenient.

In the BreakAtThreshold(), we are retrieving all the elements up to a specified index in the source sequence. In this case, Break is called, because when we reach the index on one thread, it is possible that prior elements in the source have not yet been processed. Break will cause other threads to abandon work on later seqments (if they are engaged in any) and complete the processing all prior elements before exiting the loop.

It is important to understand that after either Stop or Break is called, other threads on a loop may continue to run for some period of time, which is not in the control of the application developer. You can use the ParallelLoopState.IsStopped property to check whether the loop has been stopped on another thread. In the following example, if IsStopped is true, then no further data is written to the collection.

  • Copy and paste the code example into a Visual Studio 2010 project.

Community Additions

ADD
Show:
© 2014 Microsoft