Understanding CancellationTokenSource with Parallel

CancellationTokenSource also works with Parallel.For, Parallel.ForEach, and Parallel LINQ much like with Task. The behavior is similar, but not the same. When starting a Parallel.For or ForEach, you can pass in a ParallelOptions object that contains the CancellationToken. When starting Parallel LINQ, you can call AsParallel().WithCancellation(token). Once the Parallel operation is running, you can cancel the CancellationTokenSource to cancel it. In this case, the Parallel class is handling that cancel, not any code that you provided, so there is more consistency overall. Unless the Parallel operation has already run to its completion, the Cancel will stop it and it will throw an OperationCanceledException (not a TaskCanceledException). Here is some additional info from “Essential C# 4.0”:

Note that internally the parallel loop case prevents new iterations that haven’t started yet from commencing via the IsCancellationRequested property. Existing executing iterations will run to their respective termiation points. Furthermore, calling Cancel() even after all iterations have completed will still cause the registered cancel event (via cts.Token.Register()) to execute.

Here are some unit tests for Parallel.For, Parallel.ForEach, and Parallel LINQ to show this. Note that I placed the Parallel operations within a Task, but this is only so that I could cancel the operation while the Task was running:

[TestMethod]
public void ParallelForCancelsWithToken()
{
	CancellationTokenSource tokenSource = new CancellationTokenSource();
	var token = tokenSource.Token;
	//This is how to pass a token to Parallel.For or Parallel.ForEach
	var options = new ParallelOptions() { CancellationToken = token };
	bool parallelThrew = false;
	var outerTask = Task.Factory.StartNew(() =>
		{
			try
			{
				Parallel.For(0, 5, options, (i) => Thread.Sleep(500));
			}
			catch (Exception ex)
			{
				Assert.IsInstanceOfType(ex, typeof(OperationCanceledException));
				parallelThrew = true;
			}
		}
	);
	tokenSource.Cancel();
	outerTask.Wait();
	Assert.IsTrue(parallelThrew);
}
[TestMethod]
public void ParallelForEachCancelsWithToken()
{
	CancellationTokenSource tokenSource = new CancellationTokenSource();
	var token = tokenSource.Token;
	//This is how to pass a token to Parallel.For or Parallel.ForEach
	var options = new ParallelOptions() { CancellationToken = token };
	bool parallelThrew = false;
	var itemsToIterate = new List<string> { "one", "two", "three" };
	var outerTask = Task.Factory.StartNew(() =>
	{
		try
		{
			Parallel.ForEach(itemsToIterate, options, (i) => Thread.Sleep(500));
		}
		catch (Exception ex)
		{
			Assert.IsInstanceOfType(ex, typeof(OperationCanceledException));
			parallelThrew = true;
		}
	}
	);
	tokenSource.Cancel();
	outerTask.Wait();
	Assert.IsTrue(parallelThrew);
}
[TestMethod]
public void PlinqCancelsWithToken()
{
	CancellationTokenSource tokenSource = new CancellationTokenSource();
	var token = tokenSource.Token;
	bool plinqThrew = false;
	var itemsToIterate = new List<string> { "one", "two", "three" };

	var outerTask = Task.Factory.StartNew(() =>
	{
		try
		{
			var result = itemsToIterate.AsParallel().WithCancellation(token)
				.Select((item) =>
					{
						Thread.Sleep(500);
						return item.ToString();
					}
			);
			//Must force execution or exception will not throw
			Assert.IsTrue(result.Count() < 3);
		}
		catch (Exception ex)
		{
			Assert.IsInstanceOfType(ex, typeof(OperationCanceledException));
			plinqThrew = true;
		}
	}
	);
	tokenSource.Cancel();
	outerTask.Wait();
	Assert.IsTrue(plinqThrew);
}
Advertisements

About John Adams

Microsoft Azure specialist and trainer, software developer, ASP.NET MVC enthusiast, father, husband, Christian. Overall, a very fortunate man.
This entry was posted in Asynchrony, Parallel, Unit Tests and tagged , , . Bookmark the permalink.

2 Responses to Understanding CancellationTokenSource with Parallel

  1. Pingback: Short trip through Task, CancellationTokenSource and Parallel « a developer’s breadcrumb

  2. Johnk50 says:

    This is one awesome blog post. Keep writing. agebdfabgcka

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s