C#idiomatic C#
how-to-cancel-a-parallel-for-or-foreach-loopstandard/parallel-programming

Async and concurrency

How to: Cancel a Parallel.For or ForEach Loop

Cancel a Parallel.For or Parallel.ForEach loop in .NET by supplying a cancellation token object to the method in the ParallelOptions parameter.

  • page standard/parallel-programming/how-to-cancel-a-parallel-for-or-foreach-loop
  • updated 08/18/2023

The System.Threading.Tasks.Parallel.For and System.Threading.Tasks.Parallel.ForEach methods support cancellation through the use of cancellation tokens. For more information about cancellation in general, see Cancellation. In a parallel loop, you supply the System.Threading.CancellationToken to the method in the System.Threading.Tasks.ParallelOptions parameter and then enclose the parallel call in a try-catch block.

Example

The following example shows how to cancel a call to System.Threading.Tasks.Parallel.ForEach. You can apply the same approach to a System.Threading.Tasks.Parallel.For call.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_parallel/cs/parallel_cancel.cs29

csharp
//<snippet29>
namespace CancelParallelLoops
{
    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main()
        {
            int[] nums = Enumerable.Range(0, 10_000_000).ToArray();
            CancellationTokenSource cts = new();

            // Use ParallelOptions instance to store the CancellationToken
            ParallelOptions options = new()
            {
                CancellationToken = cts.Token,
                MaxDegreeOfParallelism = Environment.ProcessorCount
            };
            Console.WriteLine("Press any key to start. Press 'c' to cancel.");
            Console.ReadKey();

            // Run a task so that we can cancel from another thread.
            Task.Factory.StartNew(() =>
            {
                if (Console.ReadKey().KeyChar is 'c')
                    cts.Cancel();
                Console.WriteLine("press any key to exit");
            });

            try
            {
                Parallel.ForEach(nums, options, (num) =>
                {
                    double d = Math.Sqrt(num);
                    Console.WriteLine($"{d} on {Environment.CurrentManagedThreadId}");
                });
            }
            catch (OperationCanceledException e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                cts.Dispose();
            }

            Console.ReadKey();
        }
    }
}
//</snippet29>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_parallel/vb/cancelloop.vb29

vb
'<snippet29>
' How to: Cancel a Parallel.For or ForEach Loop

Imports System.Threading
Imports System.Threading.Tasks

Module CancelParallelLoops
    Sub Main()
        Dim nums() As Integer = Enumerable.Range(0, 10000000).ToArray()
        Dim cts As New CancellationTokenSource

        ' Use ParallelOptions instance to store the CancellationToken
        Dim po As New ParallelOptions
        po.CancellationToken = cts.Token
        po.MaxDegreeOfParallelism = System.Environment.ProcessorCount
        Console.WriteLine("Press any key to start. Press 'c' to cancel.")
        Console.ReadKey()

        ' Run a task so that we can cancel from another thread.
        Dim t As Task = Task.Factory.StartNew(Sub()
                                                  If Console.ReadKey().KeyChar = "c"c Then
                                                      cts.Cancel()
                                                  End If
                                                  Console.WriteLine(vbCrLf & "Press any key to exit.")
                                              End Sub)

        Try

            ' The error "Exception is unhandled by user code" will appear if "Just My Code" 
            ' is enabled. This error is benign. You can press F5 to continue, or disable Just My Code.
            Parallel.ForEach(nums, po, Sub(num)
                                           Dim d As Double = Math.Sqrt(num)
                                           Console.CursorLeft = 0
                                           Console.Write("{0:##.##} on {1}", d, Thread.CurrentThread.ManagedThreadId)
                                       End Sub)

        Catch e As OperationCanceledException
            Console.WriteLine(e.Message)
        Finally
            cts.Dispose()
        End Try

        Console.ReadKey()

    End Sub
End Module
'</snippet29>

If the token that signals the cancellation is the same token that is specified in the System.Threading.Tasks.ParallelOptions instance, then the parallel loop will throw a single System.OperationCanceledException on cancellation. This immediately stops all iterations from executing as the exception is thrown. If some other token causes cancellation, the loop will throw an System.AggregateException with an System.OperationCanceledException as an InnerException.

See also