afuna afuna - 3 months ago 29
C# Question

ParallelOptions.CancellationToken seems useless

One of the members of the ParallelOptions is CancellationToken, the value of which is meant to be accessed within the lambda function of Parallel.ForEach.

Using it requires instantiating a CacellationToken before invoking Parallel.ForEach so why can't that local variable be accessed directly within the Lambda function of the ForEach?

e.g. instead of:

var ct = new CancellationToken();
var options = new ParallelOptions { CancellationToken = ct }

Parallel.ForEach(source, options, (item) =>
{
options.ct.ThrowIfCancellationRequested();
})


why can't I just use:

var ct = new CancellationToken();

Parallel.ForEach(source, (item) =>
{
ct.ThrowIfCancellationRequested();
})


Is it just a convenient place to stash the token, or is there some underlying reason for this design?

Answer

If you tell Parallel.ForEach about the CancellationToken, it can stop processing when the token is cancelled. You may not want to throw an exception from your loop - you may just want to ignore the cancellation token yourself, and just expect that a subset of your items will be processed.

For example:

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

class Test
{
    static void Main()
    {
        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
        var items = Enumerable.Range(0, 100).ToList();
        Parallel.ForEach(items, new ParallelOptions { CancellationToken = cts.Token },
            item =>
            {
                Console.WriteLine(item);
                Thread.Sleep(1000);
            });
    }
}

This will finish after three seconds with an OperationCanceledException (rather than the AggregateException you'd receive if one/some of the individual tasks failed) - but the action body itself doesn't need to know about a cancellation token. Sure, if you have an expensive operation which you don't want to complete unnecessarily for the final items being processed you can monitor the token yourself, but otherwise you can just let Parallel.ForEach notice and throw the exception itself.

Comments