Handling exceptions in parallel loops in .NET C#

If a thread in a parallel loop throws an exception then no new iterations will be started. However, iterations already in progress are allowed to run to completion.

You can check if some other iteration has thrown an exception using the the ParallelLoopState class. This class has an IsExceptional property:

Parallel.For(0, 10, (int index, ParallelLoopState state) =>
{
	if (state.IsExceptional)
	{
		//do something
	}
});

All exceptions throw within the parallel loop are collected within an AggregateException object:

List<int> integers = new List<int>();

for (int i = 0; i <= 100; i++)
{
	integers.Add(i);
}

try
{
	Parallel.ForEach(integers, (int item, ParallelLoopState state) =>
	{
		if (item > 50)
		{
			throw new ArgumentNullException("Something has happened");
		}
		else
		{
			Console.WriteLine("Less than 50: {0}", item);
		}

		if (state.IsExceptional)
		{
			Console.WriteLine("Exception!");
		}

	});
}
catch (AggregateException ae)
{
	ae.Handle((inner) =>
	{
		Console.WriteLine(inner.Message);
		return true;
	});
}

You can read more about handling exceptions throw by tasks here, here and here.

Please view the comment section for a link with another example.

View the list of posts on the Task Parallel Library here.

Advertisement

Supply loop options to parallel loops in .NET C#

It’s possible to supply additional options to parallel for and foreach loops. The ParallelOptions class has the following properties:

  • CancellationToken: sets the cancellation to break a parallel loop
  • MaxDegreeOfParallelism: sets the max concurrency for a parallel loop. A value of -1 means no limit
  • TaskScheduler: this is normally null as the default task scheduler is very efficient. However, if you have a custom task scheduler then you can supply it here

We won’t go into building a custom task scheduler as it is a heavy topic and probably 98% of all .NET programmers are unlikely to ever need one.

So let’s see what MaxDegreeOfParallelism can do for us. It cannot do much directly other than setting a limit to the number of tasks created during a parallel loop. Note the word ‘limit’: setting a high number won’t guarantee that this many Tasks will be started. It is only an upper limit that the task scheduler will take into account. A value of 0, i.e. no concurrency at all, will cause an exception to be thrown in Parallel.For and Parallel.ForEach. A value of 1 is practically equal to sequential execution.

Declare the options object as follows:

ParallelOptions parallelOptions	= new ParallelOptions() { MaxDegreeOfParallelism = 1 };

You can supply the options to the Parallel.For and ForEach methods as input parameters:

Parallel.For(0, 10, parallelOptions, index =>
{
	Console.WriteLine("For Index {0} started", index);
	Thread.Sleep(100);
	Console.WriteLine("For Index {0} finished", index);
});

int[] integers = new int[] { 0, 2, 4, 6, 8 };

Parallel.ForEach(integers, parallelOptions, index =>
{
	Console.WriteLine("ForEach Index {0} started", index);
	Thread.Sleep(100);
	Console.WriteLine("ForEach Index {0} finished", index);
});

Run the code an you should see that the index values are processed in a sequential manner. Then set MaxDegreeOfParallelism to e.g. 5 and you should see something different.

View the list of posts on the Task Parallel Library here.

Breaking parallel loops in .NET C# using the Stop method

It’s not uncommon to break the execution of a for/foreach loop using the ‘break’ keyword. A for loop can look through a list of integers and if the loop body finds some matching value then the loop can be exited. It’s another discussion that ‘while’ and ‘do until’ loops might be a better alternative, but there you go.

You cannot simply break out from a parallel loop using the break keyword. However, we can achieve this effect with the ParallelLoopState class. Let’s say we have the following integer array…:

List<int> integers = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

…and we want to break the loop as soon as we’ve found a number higher than 5. Both Parallel.For() and Parallel.ForEach() accepts an Action of T parameter as we saw before. This Action object has an overloaded version: Action of T and ParallelLoopState. The loop state is created automatically by the Parallel class. The loop state object has a Stop() method which stops the loop execution. Or more specifically it requests the loop to be stopped and the task scheduler will go about doing that. Keep in mind that some iterations may be continued even after the call to Stop was made:

Parallel.ForEach(integers, (int item, ParallelLoopState state) =>
{
	if (item > 5)
	{
		Console.WriteLine("Higher than 5: {0}, exiting loop.", item);
		state.Stop();
	}
	else
	{
		Console.WriteLine("Less than 5: {0}", item);
	}
});

So parallel loops can be exited but not with the same precision as in the case of synchronous loops.

View the list of posts on the Task Parallel Library here.

Parallel stepped for loops in .NET C#

We saw in this post how to create a parallel for loop using the Parallel object in .NET. One drawback of that method is the we cannot create the parallel equivalent of a synchronous stepped for loop:

for (int i = 0; i < 20; i += 2)

Parallel.For accepts the start index and the end index but not the step value. Parallel.ForEach comes to the rescue: we create an integer list with only those values that should be processed. Create the stepped integer list as follows:

public IEnumerable<int> SteppedIntegerList(int startIndex,
			int endEndex, int stepSize)
{
        for (int i = startIndex; i < endEndex; i += stepSize)
	{
		yield return i;
	}
}

Then call Parallel.ForEach:

Parallel.ForEach(SteppedIntegerList(0, 20, 2), index =>
{
	Console.WriteLine("Index value: {0}", index);
});

View the list of posts on the Task Parallel Library here.

Parallel for-each loops in .NET C#

It’s trivial to create parallel for-each loops in .NET using the built-in Parallel class. It has a ForEach() method with a wide range of overloaded varieties. One of the easier ones accepts 2 parameters:

  • An IEnumerable object
  • An Action of T which is used to process each item in the list

The parallel ForEach loop will process any type of object in an IEnumerable enumeration. Example:

List<string> dataList = new List<string> 
{
         "this", "is", "random", "sentence", "hello", "goodbye"
};

Parallel.ForEach(dataList, item =>
{
	Console.WriteLine("Item {0} has {1} characters",
		item, item.Length);
});

Run the code and you’ll see that the items in the string list are not processed sequentially.

View the list of posts on the Task Parallel Library here.

Parallel for loops in .NET C#

It’s trivial to start parallel loops in .NET using the built-in Parallel class. The class has a For() method with a wide variety of overloads. One of the easier ones accepts 3 parameters:

  • The start index: this is inclusive, i.e. this will be the first index value in the loop
  • The end index: this is exclusive, so it won’t be processed in the loop
  • An Action of int which represents the method that should be executed in each loop, where int is the actual index value

Example:

Parallel.For(0, 10, index =>
{
	Console.WriteLine("Task Id {0} processing index: {1}",
		Task.CurrentId, index);
});

If you run this code then you’ll see that the index values are indeed processed in a parallel fashion. The printout on the console windows may show that index 5 is processed a bit before 4 or that thread id 1 processed index 2 and thread #2 processed index 7. This all depends on the task scheduler so use parallel loops only in case you don’t care about the actual processing order.

View the list of posts on the Task Parallel Library here.

Parallel LINQ in .NET C#: the ForAll() extension

The ForAll() extension is part of the Linq to Objects library. It allows to execute an Action on each item in the query. It can be used in conjunction with parallel queries as well to perform actions such as filtering on each item of a ParallelQuery.

The following example takes each item in an integer array, selects only the even numbers and then prints each item and it square root:

int[] integerArray = new int[50];
for (int i = 0; i < integerArray.Length; i++)
{
	integerArray[i] = i;
}

integerArray.AsParallel()
	.Where(item => item % 2 == 0)
	.ForAll(item => Console.WriteLine("Item {0} Result {1}",
		item, Math.Sqrt(item)));

View the list of posts on the Task Parallel Library here.

Parallel LINQ in .NET C#: a basic example

Parallel LINQ – PLINQ – can be applied to LINQ to Objects. This type of LINQ which works with the IEnumerable and IEnumerable of T data types. There are extension methods in the PLINQ library that make parallel processing of the result from queries available. The result is that multiple data items are processed concurrently.

In fact it is not difficult to transform a “normal” LINQ statement to PLINQ.

Set up the data source:

int[] sourceData = new int[1000];
for (int i = 0; i < sourceData.Length; i++)
{
	sourceData[i] = i;
}

We want to extract all even numbers. We also want the items to be processed in a parallel fashion. The following query will do just that:

IEnumerable<int> parallelResults =
	from item in sourceData.AsParallel()
	where item % 2 == 0
	select item;

Note the AsParallel() extension method. Behind the scenes it creates an instance of the ParallelQuery class. Without the AsParallel extension you’d be using standard LINQ features hidden in the Enumerable class. This little extension makes sure that the parallel features are used instead.

Print the results:

foreach (int item in parallelResults)
{
	Console.WriteLine("Item {0}", item);
}

Run the code and you’ll see that the integers do not follow any particular order. This is the result of the parallel execution of the query. Remove the AsParallel extension from the query and integers will be presented in an ascending order. The method hides a lot of complexity from the programmer. It is up to PLINQ to decide how the query will be parallelised, we only indicate the request through the extension. PLINQ will try to optimise the query execution based on a range of parameters. If sequential execution seems to be a better fit then the items may still be processed sequentially despite your wish.

View the list of posts on the Task Parallel Library here.

Lazy task execution in .NET C#

Tasks in .NET can be executed lazily, i.e. only at the time when the result is needed. This may never come in certain situations. You can build tasks that are not executed until a lazy variable is required. The lazy variable is not initialised until it is read.

You can construct a lazy task in two separate steps:

Func<string> functionBody = new Func<string>(() =>
{
	Console.WriteLine("Function initialiser starting...");
	return "Some return value from the function initialiser";
});

Lazy<Task<string>> lazyInitialiser = new Lazy<Task<string>>
	(() =>
		Task<string>.Factory.StartNew(functionBody)
	);

Here we first create a function which we then pass to the Lazy constructor. You can specify the type of object that will be lazily initialised. One of the overloaded constructors of Lazy accepts a Func of Task of T which will be the initialisation function. Task.Factory.StartNew comes in handy here as we can supply our function.

You can achieve the same initialisation in one step as follows:¨

Lazy<Task<string>> inlineLazyInitialiser = new Lazy<Task<string>>(
	() => Task<string>.Factory.StartNew(() =>
	{
		Console.WriteLine("Inline initialiser starting...");
		return "Some return value from the inline lazy initialiser.";
	}));

We read the results as follows:

Console.WriteLine("Calling function initialiser within Lazy...");
Console.WriteLine(string.Concat("Result from task: ", lazyInitialiser.Value.Result));

Console.WriteLine("Calling inline lazy initialiser...");
Console.WriteLine(string.Concat("Result from task: ", inlineLazyInitialiser.Value.Result));

View the list of posts on the Task Parallel Library here.

Continuing with an attached child Task in .NET C#

A child Task, a.k.a a nested Task, is a Task that’s started within the body of another Task. The containing Task is called the parent.

We saw here how to create an attached child Task and here how to create continuation tasks. We can now marry the two concepts. If you don’t know what they are then make sure to check out those posts as well.

This is how you attach a child task to a parent, create a continuation task to the child task and attach the continuation task to the parent as well:

Task parent = Task.Factory.StartNew(() =>
{
	Console.WriteLine("Starting child task...");

	Task child = Task.Factory.StartNew(() =>
	{
		Console.WriteLine("Child running. Going to sleep for a sec.");
		Thread.Sleep(1000);
		Console.WriteLine("Child finished and throws an exception.");
		throw new Exception();
	}, TaskCreationOptions.AttachedToParent);

	child.ContinueWith(antecedent =>
	{
		// write out a message and wait
		Console.WriteLine("Continuation of child task running");
		Thread.Sleep(1000);
		Console.WriteLine("Continuation finished");
	}, TaskContinuationOptions.AttachedToParent
		| TaskContinuationOptions.OnlyOnFaulted);				
});

try
{
	Console.WriteLine("Waiting for parent task");
	parent.Wait();
	Console.WriteLine("Parent task finished");
}
catch (AggregateException ex)
{
	Console.WriteLine("Exception: {0}", ex.InnerException.GetType());
}

Note the TaskCreationOptions parameter in the StartNew method: AttachedToParent. We also introduce a filter to the continuation task: run it only if the task before that, i.e. the ‘child’ has faulted. Which will happen as we throw an exception in the body of ‘child’.

If you run this code the you’ll see that the exception thrown by the child task is caught in the try-catch block. The original exception has been packaged within an AggregateException. Also, the Wait() method will wait for the parent task to finish which in turn waits for the child to finish. So Wait() indirectly waits for any attached children the parent Task may have. Finally, you’ll see that the continuation task runs as ‘child’ has faulted.

View the list of posts on the Task Parallel Library here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: