Events, delegates and lambdas in .NET C# part 2: delegates in action

Introduction

In the previous installment of this series we looked at the foundations of delegates, events and event handlers. It’s now time to see them in action.

Demo

Open Visual Studio 2012/2013 and create a new Console application with .NET 4.5 as the underlying framework. I haven’t tested the code with .NET4.0 or .NET 3.5 but I think it should work with those frameworks as well.

In Program.cs add the following delegate declaration just above the Main method:

public delegate void WorldSavedHandler(string saviourName, DateTime dateForNextCatastrophy);

So this is a delegate for when some superhero saves the world for the nth time. It is void, it has a name and accepts two parameters: a string and a date. Note that it’s customary but not mandatory to name delegates with the word ‘Handler’ attached.

Add the following methods that match the delegate signature:

static void WorldSaved(string saviour, DateTime nextTime)
{
	Console.WriteLine(string.Concat("World saved by ", saviour, ", get ready again by ", nextTime));
}

static void WorldSavedAgain(string saviour, DateTime nextTime)
{
	Console.WriteLine(string.Concat("World saved this time by ", saviour, ", get ready again by ", nextTime));
}

In Main you can instantiate a delegate and hook up a callback method as follows:

WorldSavedHandler worldSavedHandler = new WorldSavedHandler(WorldSaved);

As you type the declaration in the editor IntelliSense will indicate that the delegate is expecting a void method that accepts a string and a date. If you try to pass in a method that doesn’t match this signature you’ll get a compile error. Add another delegate declaration to Main:

WorldSavedHandler worldSavedAgainHandler = new WorldSavedHandler(WorldSavedAgain);

We have set up two pipelines: worldSavedHandler which sends its data to WorldSaved and worldSavedAgainHandler which sends its data to WorldSavedAgain. We can invoke the delegates the same way as we normally call a method:

worldSavedHandler("Bruce Willis", DateTime.UtcNow.AddYears(2));
worldSavedAgainHandler("Spiderman", DateTime.UtcNow.AddMonths(3));

Console.ReadKey();

I added the call to ReadKey so that the console window doesn’t just close down.

Run the program and you’ll see the appropriate output in the console window.

Why on earth are we bothering with this? Up to now this looks like some funny way of calling methods. Consider the following method which accepts our delegate type:

static void SaveWorld(WorldSavedHandler worldSaved)
{
	worldSaved("Bruce Willis", DateTime.UtcNow.AddMonths(5));
}

In Main comment out the two delegate calls…:

worldSavedHandler("Bruce Willis", DateTime.UtcNow.AddYears(2));
worldSavedAgainHandler("Spiderman", DateTime.UtcNow.AddMonths(3));

…and add the following instead:

SaveWorld(worldSavedHandler);

This is stating that we need to save the world and we have 2 possible delegates to give this work to. We entrust worldSavedHandler with this task so we pass it in. An analogy from the real world is when a boss decides the software development team needs to build the data access layer and appoints a particular team member to do just that. In other words the boss delegates the job to a particular programmer, i.e. a handler.

We know that worldSavedHandler will call WorldSaved. SaveWorld gets the delegate and will invoke whatever was passed to it. SaveWorld does NOT know which handler the delegate will call. From its point of view it could be any method with the correct signature, so it’s quite a dynamic setup. The delegate could have been passed in from whichever external object to the SaveWorld method. SaveWorld passes in Bruce Willis and now + 3 months to some unknown method.

Run the program and you’ll see that WorldSaved was called. Now, without changing SaveWorld directly, you can modify its outcome just by providing a different delegate:

SaveWorld(worldSavedAgainHandler);

Run the program and WorldSavedAgain was called as expected without changing SaveWorld at all.

Let’s look at multicasting quickly. Comment out the call to SaveWorld in Main and instead add a third delegate declaration:

WorldSavedHandler worldSavedOnceMoreHandler = new WorldSavedHandler(WorldSavedOnceMore);

…where WorldSavedOnceMore looks like this:

static void WorldSavedOnceMore(string saviour, DateTime nextTime)
{
	Console.WriteLine(string.Concat("World saved once again by ", saviour, ", get ready again by ", nextTime));
}

There are different way to build an invocation list. The following chains our three delegates together:

worldSavedHandler += worldSavedAgainHandler;
worldSavedHandler += worldSavedOnceMoreHandler;

worldSavedHandler("Superman", DateTime.UtcNow.AddMonths(3));

You’ll see that all three method handlers were called.

The below is equivalent to the above:

worldSavedHandler += worldSavedAgainHandler + worldSavedOnceMoreHandler;
worldSavedHandler("Superman", DateTime.UtcNow.AddMonths(3));

The following will wipe out the first handler:

worldSavedHandler = worldSavedAgainHandler + worldSavedOnceMoreHandler;
worldSavedHandler("Superman", DateTime.UtcNow.AddMonths(3));

This time only WorldSavedAgain and WorldSavedOnceMore are called.

You’ll also see that each handler is called in the order they were added to the invocation list. This way we can invoke multiple handlers by calling just one delegate, in this case worldSavedHandler.

Returning a value from a delegate

Delegates can return values, they don’t need to be void all the time. Add the following delegate to Program.cs:

public delegate string SomeoneJustShoutedHandler(string who, DateTime when);

Add the following handlers:

static string SomeoneShouted(string who, DateTime when)
{
	return string.Concat(who , " has shouted on ", when);
}

static string SomeoneShoutedAgain(string who, DateTime when)
{
	return string.Concat(who, " has shouted on ", when);
}

static string SomeoneShoutedOnceMore(string who, DateTime when)
{
	return string.Concat(who, " has shouted once more on ", when);
}

…and 3 delegate declarations with the delegate chain:

SomeoneJustShoutedHandler someoneShoutedHandler = new SomeoneJustShoutedHandler(SomeoneShouted);
SomeoneJustShoutedHandler someoneShoutedAgainHandler = new SomeoneJustShoutedHandler(SomeoneShoutedAgain);
SomeoneJustShoutedHandler someoneShoutedOnceMoreHandler = new SomeoneJustShoutedHandler(SomeoneShoutedOnceMore);

someoneShoutedHandler += someoneShoutedAgainHandler + someoneShoutedOnceMoreHandler;
string finalShout = someoneShoutedHandler("The neighbour", DateTime.UtcNow);
Console.WriteLine("Final shout: " + finalShout);

Run the code and you’ll see that it’s only the third string that’s returned. It’s logical as we cannot have multiple return values in C#. You can get the individual return values by calling the delegates separately. However, with an invocation list the last delegate in the list will win.

Adding events

We’ve seen that using delegates can work fine without adding events to the code. However, delegates coupled with events help us raise those events and propagate the event arguments to all listeners in the invocation list.

An event is declared using the ‘event’ keyword in C#. It is also declared with the type of the delegate that raises the event as an event by itself cannot do much. Events are the standard way of providing notifications to all listeners. The data will be supplied using the delegate declared in conjunction with the event. In .NET programming you’ll probably rarely see delegates used without events although it’s certainly possible as we’ve seen above. You also give a name to the event. Here’s an example of an event declaration:

public event WorldSavedHandler WorldHasBeenSaved;

Listeners will be able to sign up with the event and receive a notification when the event has fired. It is equivalent to building the invocation list we saw above. So an event is really nothing but a wrapper around a delegate.

There’s also a more complex way of defining an event using the add/remove accessors:

private WorldSavedHandler WorldHasBeenSaved;
public event WorldSavedHandler WorldSaved
{
	[MethodImpl(MethodImplOptions.Synchronized)]
	add
	{
		WorldHasBeenSaved = (WorldSavedHandler)Delegate.Combine(WorldHasBeenSaved, value);
	}
	[MethodImpl(MethodImplOptions.Synchronized)]
	remove
	{
		WorldHasBeenSaved = (WorldSavedHandler)Delegate.Remove(WorldHasBeenSaved, value);
	}
}

This provides more control over how listeners are added to and removed from the invocation list. You’ll probably not see this kind of event declaration too often. However, if you see it somewhere then you’ll now know what it means.

We have a private delegate of type WorldSavedHandler. Then we see the event declaration that conforms to the example provided above. The add accessor defines how a listener is added to the invocation list. The remove accessor does the opposite. The actions are synchronised to make sure that if different threads perform the same action we don’t run into concurrency issues. The ‘value’ keyword indicates the listener that’s going to be added to the invocation list. The Delegate.Combine method takes the delegate we already have and adds in the new one. The remove accessor works with the Delegate.Remove method that removes ‘value’ from the invocation list.

You might use this type of event declaration if you have extra logic on who and when is allowed to be listed on the invocation list. The access may be limited to some special objects that fulfil certain rules.

Now that we know more about events than we ever wanted to we can create some custom events in our code. We’ll actually do that in the next part.

Events, delegates and lambdas in .NET C# part 1: delegate basics

Introduction

I believe that learning the basic features of a popular OO language, such as C# or Java is not too difficult. Obviously you’ll need a positive mindset towards programming in general otherwise you’re bound to fail but that’s true for every discipline. The basic structures found in C#, such as classes, properties, constructors, methods, functions, interfaces and the like are quite straightforward and simple to use even after a couple of months’ worth of training. If you have a Java or C++ background then you can start to be productive with C# after some weeks even.

However, there are some language features that stick out a little as they are slightly more cryptic: events, delegates and lambda expressions. You can certainly come up with others, but my personal experience is that learning these features thoroughly takes more time than the ones listed above.

This series of posts will be dedicated to these language elements of C#.

The building blocks

If you’ve ever done any Windows app development then you must have seen examples of events and event handlers: button click event, list selection changed event, on mouse over event etc. You added an event and an event handler to a button just by double-clicking on it in the visual editor and the following elements were wired together for you in the background:

  • Event: notifications originating from objects. These notifications can be used to notify other elements – objects – in the application that may be interested in the event, such as when a new order was placed or a party is about to start. Then the objects listening to those events can perform some work dedicated to them: serve the drinks, order the pizzas, let in the guests etc.
  • Event raiser, a.k.a. the Sender: the object that raises the event. They typically don’t know anything about the objects listening to the events raised by themselves
  • Event handler: the handler that ‘caught’ the event raised by the button
  • Delegate: the glue between the button and the event handler. This is usually hidden in the designer cs file of the Form created by Visual Studio, so you may not even have come across it. Without delegates the event raised would not get to the listening objects and their event handlers
  • Event arguments, or EventArgs: the arguments that the Sender is sending to the event handler with the event raised. This can include anything important for the handler, such as the selected item index, the venue for the party, the items that were ordered etc.

Probably the most confusing element in this list is delegates. They are typically described by adjectives such as ‘odd’, ‘strange’ and ‘unusual’. A delegate, also called a function pointer, is a specialised class that derives from the MulticastDelegate base class. As noted above, it connects the event with the event handler. It’s called a function pointer because the event handler will be a function, i.e. a method, and it points at the event handler from the event and wires the event arguments in between.

An event handler will process the data received from a delegate. It is a method with a special signature that must accept a sender of type ‘object’ and an EventArgs parameter or one that’s derived from the EventArgs class. EventArgs objects are normal C# objects with properties. You can easily define your custom event args by deriving from the EventArgs class. You can put in all sorts of properties into your event args that may be interesting for those waiting for the event. A button click event handler may look like this:

public void btnOrderItems_Click(object sender, EventArgs e)
{
      //do something
}

A custom event handler can have the following form:

public void OrderPlaced(object sender, OrderPlacedEventArgs e)
{
      //do something with the order
}

Delegates

You can create delegates with the ‘delegate’ keyword in C#, example:

public delegate void GraphClicked(GraphType graphType, Point clickCoordinate);

It’s void as nothing is returned, data is only sent one way. In this example the delegate’s name is GraphClicked and it accepts two parameters: a graph type and the click coordinates. This declaration is a blueprint for the method, i.e. the handler, that will receive the data from the delegate. The handler method must match this signature otherwise the code will not compile: it must also receive a graph type and the click coordinates. An example of a matching handler:

public void GraphClicked_Handler(GraphType type, Point coords)
{
     //do something
}

Note that the parameter names don’t necessarily match those in the delegate, but the types must match.

The delegate keyword is special in .NET as the compiler does some magic when it sees it declared. It creates another object that inherits from the MulticastDelegate class behind the scenes which in turns derives from the Delegate base class. Multicast delegate means that a single message can be sent to a range of objects through a range of pipelines. Note that you cannot directly derive from the Delegate or the MulticastDelegate classes. You do it indirectly using the delegate keyword and the compiler will do the inheritance trick for you.

The list of pipelines of a multicast delegate is called the Invokation List. The delegates listed here are invoked sequentially.

Now that we have a delegate you can easily instantiate one:

GraphClicked graphClicked = new GraphClicked(GraphClicked_Handler);

So you see a delegate is a class that you can instantiate using the new keyword. Notice that you must specify the handler in the constructor of the delegate. We say that when the graphClicked delegate is invoked then the GraphClicked_Handler method should be called.

You can invoke the graphClicked delegate as follows:

graphClicked(GraphType.Line, new Point(1,1));

So you invoke it like a method and provide the necessary parameters. This adds one delegate item to the invocation list.

There’s nothing stopping you from instantiating several delegates of the same type:

GraphClicked niceGraphClicked = new GraphClicked(NiceGraphClicked_Handler);
GraphClicked uglyGraphClicked = new GraphClicked(UglyGraphClicked_Handler);

These delegates point to two different handlers: NiceGraphClicked_Handler and UglyGraphClicked_Handler. What if you want niceGraphClicked not only invoke NiceGraphClicked_Handler but UglyGraphClicked_Handler as well? In other words you want to extend the invocation list of niceGraphClicked to two items: NiceGraphClicked_Handler but UglyGraphClicked_Handler. It couldn’t be simpler:

niceGraphClicked += uglyGraphClicked;

So now when niceGraphClicked is raised…:

niceGraphClicked(GraphType.Cylinder, new Point(2, 3));

…then both NiceGraphClicked_Handler but UglyGraphClicked_Handler will be called in the same order as they were added to the invocation list.

This post has given you the absolute basics of delegates, events and event handlers. We’ll look at a couple of examples from scratch in the next post.

Exception handling in the .NET Task Parallel Library with C#: using Handle()

In this post we saw how to catch unhandled exceptions thrown by tasks. Recall that the AggregateException object has a list of inner exceptions thrown by the tasks that are waited on.

You may want to handle each type of exception in a different way. There may be types of exception that you can handle locally and some other unexpected ones that should be propagated further up the stack trace. The AggregateException object has a Handle() method where you can specify the action taken depending on the type of the inner exception. The function should return true if the exception can be dealt with locally and false if it should be propagated upwards.

Construct two tasks using the cancellation token in the first one:

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

Task firstTask = Task.Factory.StartNew(() =>
{
	cancellationToken.WaitHandle.WaitOne(-1);
	throw new OperationCanceledException(cancellationToken);
}, cancellationToken);

Task secondTask = Task.Factory.StartNew(() =>
{
	throw new NullReferenceException();
});

The effect of WaitHandle.WaitOne(-1) is to wait forever until the token has been cancelled.

Cancel the token which will interrupt the first task:

cancellationTokenSource.Cancel();

Wait for the tasks, catch the AggregateException and call its Handle method:

try
{
	Task.WaitAll(firstTask, secondTask);
}
catch (AggregateException ex)
{
	ex.Handle((innerException) =>
	{
		if (innerException is OperationCanceledException)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
}

We declare that OperationCanceledException is an expected type and can be dealt with locally. All other types should be re-thrown.

If you run the above code in a console app make sure to run it without debugging (Ctrl + F5), otherwise the code execution will unnecessarily stop when the OperationCanceledException is thrown in the first task.

You’ll see that the NullReferenceException is re-thrown as we return false for all types except for the OperationCanceledException type. Test returning true instead: the exception will be “swallowed” within the Handle method.

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

Exception handling in the .NET Task Parallel Library with C#: the basics

Handling exceptions in threading is essential. Unhandled exceptions can cause your application to exit in an unexpected and unpredictable way.

If a task throws an exception that’s not caught within the task body then the exception remains hidden at first. It bubbles up again when a trigger method is called, such as Task.Wait, Task.WaitAll, etc. The exception will then be wrapped in an AggregateException object which has an InnerException property. This inner exception will hold the actual type of exception thrown by the tasks that are waited on.

Construct and start the tasks:

Task firstTask = Task.Factory.StartNew(() =>
{
	ArgumentNullException exception = new ArgumentNullException();
	exception.Source = "First task";
	throw exception;
});
Task secondTask = Task.Factory.StartNew(() =>
{
	throw new ArgumentException();
});
Task thirdTask = Task.Factory.StartNew(() =>
{
	Console.WriteLine("Hello from the third task.");
});

Wait for all the tasks, catch the aggregate exception and analyse its inner exceptions:

try
{
	Task.WaitAll(firstTask, secondTask, thirdTask);
}
catch (AggregateException ex)
{
	foreach (Exception inner in ex.InnerExceptions)
	{
		Console.WriteLine("Exception type {0} from {1}",
			inner.GetType(), inner.Source);
	}
}

Note: if you run the above code in Debug mode in a console application then the execution will stop at each exception thrown. To simulate what’s going to happen in a deployed application make sure you run the code without debugging (Ctlr + F5).

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

Share data between Tasks using locks in .NET C# in separate regions

In the this post we saw how to use the ‘lock’ keyword to restrict access to a single critical region. You can reuse the same lock object to synchronise access to the shared variable in separate regions. The modified version of the code in the referenced post looks as follows with two critical regions:

BankAccount account = new BankAccount();

List<Task> incrementTasks = new List<Task>();
List<Task> decrementTasks = new List<Task>();

object lockObject = new object();

for (int i = 0; i < 5; i++)
{
	incrementTasks.Add(Task.Factory.StartNew(() =>
	{
		for (int j = 0; j < 1000; j++)
		{
			lock (lockObject)
			{
				account.Balance++;
			}
		}
	}));
}

for (int i = 0; i < 5; i++)
{
	decrementTasks.Add(Task.Factory.StartNew(() =>
	{
		for (int j = 0; j < 1000; j++)
		{
			lock (lockObject)
			{
				account.Balance--;
			}
		}
	}));
}

Task.WaitAll(incrementTasks.ToArray());
Task.WaitAll(decrementTasks.ToArray());

Console.WriteLine(string.Concat( "Expected value: 0, actual value: ", account.Balance));

We have two sets of tasks: one that increases the balance and another that reduces it. By reusing the same lock object in both cases we can ensure that it’s always at most one task that has access to the shared variable.

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

Thread safe collections in .NET: ConcurrentQueue

Concurrent collections in .NET work very much like their single-thread counterparts with the difference that they are thread safe. These collections can be used in scenarios where you need to share a collection between Tasks. They are typed and use a lightweight synchronisation mechanism to ensure that they are safe and fast to use in parallel programming.

Concurrent queues

If you don’t know what Queues are then you can read about them here. The Queue of T generic collection has a thread-safe counterpart called ConcurrentQueue. Important methods:

  • Enqueue(T element): adds an item of type T to the collection
  • TryPeek(out T): tries to retrieve the next element from the collection without removing it. The value is set to the out parameter if the method succeeds. Otherwise it returns false.
  • TryDequeue(out T): tries to get the first element. It removes the item from the collection and sets the out parameter to the retrieved element. Otherwise the method returns false

The ‘try’ bit in the method names implies that your code needs to prepare for the event where the element could not be retrieved. If multiple threads retrieve elements from the same queue you cannot be sure what’s in there when a specific thread tries to read from it.

Example

Declare and fill a concurrent queue:

ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();

for (int i = 0; i < 5000; i++)
{
	concurrentQueue.Enqueue(i);
}

We’ll want to get the items from this collection and check if all of them have been retrieved using a counter. The counter will also be shared among the threads using the ‘lock’ technique we saw in this post – or actually something that is similar to the ‘lock’ keyword: the Interlocked class. Interlocked has an Increment method which accepts a ref int parameter. It will increment the incoming integer in an atomic operation.

int itemCount = 0;

Task[] queueTasks = new Task[20];
for (int i = 0; i < queueTasks.Length; i++)
{
	queueTasks[i] = Task.Factory.StartNew(() =>
	{
		while (concurrentQueue.Count > 0)
		{
			int currentElement;
			bool success = concurrentQueue.TryDequeue(out currentElement);
			if (success)
			{
				Interlocked.Increment(ref itemCount);
			}
		}
	});
}

The while loop will ensure that we’ll try to dequeue the items as long as there’s something left in the collection.

Wait for the tasks and print the number of items processed – the counter should have the same value as the number of items in the queue:

Task.WaitAll(queueTasks);
Console.WriteLine("Counter: {0}", itemCount);

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

Suspending a Task using Thread.Sleep in .NET C#

You may want to put a Task to sleep for some time. You might want to check the state of an object before continuing. The Task continues after the sleep time.

One way to solve this is using the classic Thread.Sleep method since the Task library uses .NET threading support in the background.

First construct your cancellation token:

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

Use this token in the constructor of the task:

Task task = Task.Factory.StartNew(() =>
{
	for (int i = 0; i < 100000; i++)
	{
		Thread.Sleep(10000);
		Console.WriteLine(i);
		cancellationToken.ThrowIfCancellationRequested();
	}
}, cancellationToken);

Note the Sleep method where we suspend the Task for 10 seconds.

You can use the cancellation token to interrupt the task:

cancellationTokenSource.Cancel();

In the previous post we discussed how to suspend a task using the WaitOne method of the cancellation token. The main difference between WaitOne and Sleep is that Sleep will block even if the task is cancelled whereas WaitOne will exit if the cancellation come before the sleep time is over.

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

Suspending a Task using a CancellationToken in .NET C#

You may want to put a Task to sleep for some time. You might want to check the state of an object before continuing. The Task continues after the sleep time.

One way to solve this is using the WaitOne method of the WaitHandle property of the CancellationToken object.

Construct the CancellationToken object as follows:

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

The WaitOne() method without parameters suspends the Task execution until the Cancel() method of the CancellationToken object has been called. It has two overloads where you can specify some time: the amount of time to wait in milliseconds or some TimeSpan. The task suspension lasts until the sleep time is over OR the Cancel() method on the cancellation token has been called, whichever happens first. The WaitOne method also has a boolean return value where ‘true’ means that the Task has been cancelled and false means the the allotted sleep time was over.

You can use the cancellation token and the WaitOne method as follows:

Task task = Task.Factory.StartNew(() =>
{
	for (int i = 0; i < 1000000; i++)
	{
		bool cancelled = cancellationToken.WaitHandle.WaitOne(10000);
		Console.WriteLine("Value {0}. Cancelled? {1}", i, cancelled);
		if (cancelled)
		{
			throw new OperationCanceledException(cancellationToken);
		}
	}
}, cancellationToken);

You can cancel the task as follows:

cancellationTokenSource.Cancel();

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

Thread safe collections in .NET: ConcurrentBag

Concurrent collections in .NET work very much like their single-thread counterparts with the difference that they are thread safe. These collections can be used in scenarios where you need to share a collection between Tasks. They are typed and use a lightweight synchronisation mechanism to ensure that they are safe and fast to use in parallel programming.

Concurrent bags

Concurrent bags are similar to concurrent stacks and concurrent queues but there’s a key difference. Bags are unordered collections. This means that the order of the items is not the same as how they were inserted. So concurrent bags are ideal if you would like to share a List of T generic collection among several tasks.

Important methods:

  • Add(T element): adds an item of type T to the collection
  • TryPeek(out T): tries to retrieve the next element from the collection without removing it. The value is set to the out parameter if the method succeeds. Otherwise it returns false.
  • TryTake(out T): tries to get the first element. It removes the item from the collection and sets the out parameter to the retrieved element. Otherwise the method returns false

The ‘try’ bit in the method names imply that your code needs to prepare for the event where the element could not be retrieved. If multiple threads retrieve elements from the same list you cannot be sure what’s in there when a specific thread tries to read from it.

Example

The example is almost identical to what we saw for the collections discussed previously in the posts on TPL.

Declare and fill a concurrent bag:

ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();

for (int i = 0; i < 5000; i++)
{
	concurrentBag.Add(i);
}

Next we’ll try to take every item from the bag. The bag will be accessed by several tasks at the same time. The counter variable – which is also shared and synchronised- will be used to check if all items have been retrieved.

int counter = 0;

Task[] bagTasks = new Task[20];
for (int i = 0; i < bagTasks.Length; i++)
{
	bagTasks[i] = Task.Factory.StartNew(() =>
	{
		while (concurrentBag.Count > 0)
		{
			int bagElement;
			bool success = concurrentBag.TryTake(out bagElement);
			if (success)
			{
				Interlocked.Increment(ref counter);
			}
		}
	});
}

The while loop will ensure that we’ll try to take the items as long as there’s something left in the collection.

Wait for the tasks and print the number of items processed – the counter should have the same value as the number of items in the bag:

Task.WaitAll(bagTasks);
Console.WriteLine("Counter: {0}", counter);

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

Share data between Tasks using locks in .NET C#

You need to be careful when sharing mutable data across several tasks. If you don’t restrict the access to a shared variable then the tasks will all modify it in an unpredictable way. It’s because you cannot be sure how the tasks will be started and managed by the operating system: it depends on a range of parameters, such as the CPU usage, available memory, etc. If each task is supposed to modify a variable then maybe the first task won’t finish its work before the second task reads the same variable.

Consider the following:

class BankAccount
{
	public int Balance { get; set; }
}

The following code builds a list of tasks that each increase the balance by 1:

BankAccount account = new BankAccount();
List<Task> tasks = new List<Task>();

for (int i = 0; i < 5; i++)
{
	tasks.Add(Task.Factory.StartNew(() =>
	{
		for (int j = 0; j < 1000; j++)
		{
			account.Balance = account.Balance + 1;
		}
	}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine(string.Concat("Expected value 5000", ", actual value ",account.Balance));

We expect the final balance to be 5000. Run the code and you will most likely get something less, such as 3856 or 4652. At times you get exactly 5000 but it’s unpredictable.

In such cases you need to identify the volatile critical regions in your code. In this example it’s simple:

account.Balance = account.Balance + 1;

It is the account balance that is modified by each thread so we need to lock it for access so that only one thread can modify it at a certain time. You can take the word “lock” almost literally as we use the lock keyword to do that. We take an object which will act as the synchronisation primitive. The lock object must be visible to all tasks:

object lockObject = new object();

You use the lock keyword as follows:

for (int i = 0; i < 5; i++)
{
	tasks.Add(Task.Factory.StartNew(() =>
	{
		for (int j = 0; j < 1000; j++)
		{
			lock (lockObject)
			{
				account.Balance = account.Balance + 1;
			}
		}
	}));
}

The volatile region of the code is locked using the lock object. The lock is released as soon as the current task has finished working with it.

Run the modified code and you should get the correct result.

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

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