Multithreading in C# .Net – part two

Sync

In part one I described four different threading methods used in my multithreaded application.  The application consists of three threads that execute at different frequencies, processing different logic on each thread.  The methods that I covered included Thread, ThreadPool, Task and BackgroundWorker.  In this post I will detail the various synchronisation methods used.

Synchronisation Methods

When looking at the various synchronisation methods that were available for each threading method, I noticed that they could be separated into three categories: Callback, Locking and Signalling.  Granted in certain instances the categories didn’t accurately describe the operations, but on the whole I found it useful to categorise the different methods like this.  It also made writing the tests easier :).

Callback

Thread

This was one instance where the synchronisation method used didn’t quiet match up with the category.  I used the Thread.Join() method to synchronise the invocations of each thread:

ThreadPool

For the ThreadPool I used the RegisterWaitForSingleObject.  You specify a WaitHandle to wait for before executing the delegate that you pass.  A Mutex should not be used as the WaitHandle to wait for because it has thread affinity, where only the thread that acquired the Mutex can release it.  By default the callback is executed on a separate ThreadPool thread.  Therefore I used an AutoResetEvent:

The remarks section on MSDN for the RegisterWaitForSingleObject recommend storing the RegisteredWaitHandle from calling the function.  This is for cancellation purposes if the callback is to be executed more than once.  As I was passing true as the last parameter the only reason for me to store the RegisteredWaitHandle would be to call Unregister(null) on it for optimising Garbage Collection (GC).  As GC performance wasn’t an issue I decided against doing so to avoid cluttering the code.

Task

Task has a method that allows you to create a continuation to be executed when the current
Task completes.  I thought that this was by far the most elegant method of specifying a callback:

BackgroundWorker

BackgroundWorker also had a concise way of specifying a callback through the RunWorkerCompleted event.  I assigned the delegate that I wanted to execute to that event:

Locking

For the Locking synchronisation method I decided to use a thread-safe container instead of a locking primitive.  I chose the ConcurrentDictionary as it provides fine grained locking for write operations and is lock free for reading.  I also chose it because I required the ability to access specific elements using an index.  The main issue that I found using this method of synchronisation was that it didn’t provide any guarantees in relation to the order in which the various threads were given access to the collection.

Signalling

For the Signalling synchronisation method I decided to use the AutoResetEvent primitive.  This was an easy choice because of the automatic nature of the primitive.  You call WaitOne when you want to wait to be signalled by the EventWaitHandle.  You call Set on the EventWaitHandle to change it to be signalled and release one thread that is waiting on the EventWaitHandle.  After calling Set the EventWaitHandle automatically reverts to being unsignalled.

Summary

I decided to categorise the various synchronisation methods into three categories.  Locking proved to be unsuitable for this application purely because I had no control over the order in which the locking occurred.  The same signalling primitive could be used for all threading methods, but the callback methods varied for each threading method.  In the final post I will discuss the validation that I did and also describe alternative threading methods that I deemed unsuitable for this program.

Tagged , , , , . Bookmark the permalink.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *