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.

Example

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.

' How to: Stop or Break from a Parallel.For Loop 
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks

Module ParallelForStop
    Sub Main()
        StopLoop()
        BreakAtThreshold()

        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub 

    Sub StopLoop()
        Console.WriteLine("Stop loop...")
        Dim source As Double() = MakeDemoSource(1000, 1)
        Dim results As New ConcurrentStack(Of Double)()

        ' i is the iteration variable. loopState is a  
        ' compiler-generated ParallelLoopState
        Parallel.For(0, source.Length, Sub(i, loopState)
                                           ' Take the first 100 values that are retrieved 
                                           ' from anywhere in the source. 
                                           If i < 100 Then 
                                               ' Accessing shared object on each iteration 
                                               ' is not efficient. See remarks. 
                                               Dim d As Double = Compute(source(i))
                                               results.Push(d)
                                           Else
                                               loopState.[Stop]()
                                               Exit Sub 

                                           End If 
                                           ' Close lambda expression. 
                                       End Sub)
        ' Close Parallel.For
        Console.WriteLine("Results contains {0} elements", results.Count())
    End Sub 


    Sub BreakAtThreshold()
        Dim source As Double() = MakeDemoSource(10000, 1.0002)
        Dim results As New ConcurrentStack(Of Double)()

        ' Store all values below a specified threshold.
        Parallel.For(0, source.Length, Function(i, loopState)
                                           Dim d As Double = Compute(source(i))
                                           results.Push(d)
                                           If d > 0.2 Then 
                                               ' Might be called more than once!
                                               loopState.Break()
                                               Console.WriteLine("Break called at iteration {0}. d = {1} ", i, d)
                                               Thread.Sleep(1000)
                                           End If 
                                           Return d
                                       End Function)

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

    Function Compute(ByVal d As Double) As Double 
        'Make the processor work just a little bit. 
        Return Math.Sqrt(d)
    End Function 


    ' Create a contrived array of monotonically increasing 
    ' values for demonstration purposes.  
    Function MakeDemoSource(ByVal size As Integer, ByVal valToFind As Double) As Double()
        Dim result As Double() = New Double(size - 1) {}
        Dim initialval As Double = 0.01
        For i As Integer = 0 To size - 1
            initialval *= valToFind
            result(i) = initialval
        Next 
        Return result
    End Function 
End Module
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 method. 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 before the current iteration on other threads, to stop as soon as is convenient.

In the BreakAtThreshold() method, we retrieve 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 earlier elements in the source have not yet been processed. Break will cause other threads to abandon work on later segments (if they are engaged in any) and complete the processing of all prior elements before exiting the loop.

Note that you cannot control whether other threads on a loop continue to run after either Stop or Break is called. You can use the ParallelLoopState.IsStopped property to check whether the loop has been stopped on another thread.

Compiling the Code

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

See Also

Reference

System.Action<T1, T2>

Concepts

Lambda Expressions in PLINQ and TPL

Other Resources

Data Parallelism (Task Parallel Library)

Parallel Programming in the .NET Framework