Cancelling a PLINQ loop
The following code presents some simple code that demonstrates how to cancel a PLINQ loop.
//Create a cancellation token source var cts = new CancellationTokenSource(); var nums = Enumerable.Range(0, 100); //Support cancellation token source var result = nums.AsParallel().WithCancellation(cts.Token).Select(n => Math.Pow(n, 2)); //create a task that enumerates the PLINQ query above var enumTask = Task.Factory.StartNew(() => { try { foreach (var r in result) { Console.WriteLine("Got result: {0}", r); Thread.Sleep(100); //slow things a bit down } } catch (OperationCanceledException oce) { Console.WriteLine("Caught exception of type: {0}, message: {1}", oce.GetType().Name, oce.Message); } }); //create a cancelling task var cancellingTask = Task.Factory.StartNew(() => { Thread.Sleep(500); cts.Cancel(); }); //Wait for both tasks Task.WaitAll(enumTask, cancellingTask); //Wait for user input before exiting Console.WriteLine("Press any key to continue .."); Console.ReadKey();Note that although cancelling the token source with the Cancel method, this does not mean that no iterations will be executed afterwards. This will vary upon the amount of iterations already started in parallel. In PLINQ, use the WithCancellation method to supply a cancellation token to use for cancelling the PLINQ Query when it is enumerated (executed). Make note that cancelling a PLINQ query with a cancellation token will actually create an OperationCanceledException. This must be caught in a try-catch block.
Breaking a Parallel For or Parallel Foreach loop
It is possible to break a parallel for loop using the Break method of the ParallelLoopState object passed in, which means we must use specific overload(s) of the Parallel.For and Parallel.ForEach method of the Parallel class in TPL. Let's investigate the Break() method of a Parallel.ForEach loop:var nums = Enumerable.Range(0, 10000); var parallelLoopResult = Parallel.ForEach(nums, (int n, ParallelLoopState state) => { var item = Math.Pow(n, 2); Console.WriteLine("Got item {0}", item); if (item > 1000) state.Break(); }); Console.WriteLine(parallelLoopResult.IsCompleted); Console.WriteLine(parallelLoopResult.LowestBreakIteration.Value); //Wait for user input before exiting Console.WriteLine("Press any key to continue .."); Console.ReadKey();In the code above, note that in addition to the usual Parallel.ForEach index being passed in the lambda action, one also passes the ParallelLoopState. In addition, both Parallel.For and Parallel.ForEach will return a ParallelLoopResult. From this result it is possible to get different information such as if the loop actually was completed and the LowestBreakIteration.
Stopping a parallel loop
Let's look at also stopping a Parallel.For loop with the Stop method.
var parallelLoopResult = Parallel.For(0, 10000, (int n, ParallelLoopState state) => { var item = Math.Pow(n, 2); Console.WriteLine("Got item {0}", item); if (item > 1000000) state.Stop(); }); Console.WriteLine(parallelLoopResult.IsCompleted); Console.WriteLine(parallelLoopResult.LowestBreakIteration.HasValue); //Wait for user input before exiting Console.WriteLine("Press any key to continue .."); Console.ReadKey();So to summarize, cancelling a PLINQ query is possible using the WithCancellation method providing a CancellationToken from a CancellationTokenSource. To exit early a Parallel For or a Parallel ForEach method, use the Stop() or the Break() method of the ParallelLoopState. The ParallelLoopResult object returned from executing a Parallel For or Parallel ForEach loop contains information about the execution of the parallel loop, such as if it is completed and the LowestBreakIteration. The last value is a nullable int. If it has no value, the break method is not executed. If IsCompleted is false, either Stop or Break is executed. If IsCompleted is false and LowestBreakIteration has no value, Stop was called. If IsCompleted is false and LowestBreakIteration has value, Break was called. If both IsCompleted is true and LowestBreakIteration HasValue is false - which means the parallel loop executed in ordinary manner and neither Stop or Cancel was called on the ParallelLoopState.