Multithreading in C# .Net – part three

concurrency testing framework

In the part one and part two I have described various threading and synchronisation methods that could be used to solve the scenario in the program.  In this post I will detail how I validated the code through xUnit and CHESS.  I will also describe other threading methods that weren’t suitable in this particular situation.

Validation

Unit Tests

I decided to use xUnit in order to write the unit tests to validate my various implementations.  I chose it because of the Theory attribute.  This allows me to specify multiple data sources for a single method, essentially creating a single test per data source.  As a result I only had to write one test method which would then be executed for each combination of threading and synchronisation method:

For the tests I mocked the random number generator by inheriting from Random, in order to produce specific numbers so that I was able to validate the results.  I also wanted to avoid the pattern of using Thread.Sleep() with an arbitrary time before validating results.  I was able to achieve this by using AutoResetEvents, creating one for each iteration and then waiting until all have been set before validating the results.

xUnit does have support for catching exceptions from Tasks through Assert.ThrowsAsync() but I didn’t the need to use this.

CHESS

CHESS (or as a friend of mine called it, Angry Chess) is an application that can be used to track down Heisenbugs in concurrent applications.  It does so by controlling the thread scheduling for your application.  This is achieved through DLL shimming.  CHESS transparently intercepts API calls and redirects them its wrapped version of the same calls.  These wrapped methods contain semaphores which takes the scheduling control away from the OS.  It appears that CHESS is no longer in development, which is disappointing as it looks like it could be really useful.  It supports up to .Net Framework 4, excluding BackgroundWorker and Tasks.  It does have support for some of the TPL such as Parallel.For/ForEach/Invoke.

I downloaded the source for CHESS from Codeplex and had to make a few changes to the native code in order to get the solution to build in Visual Studio 2013.  One of the projects in the solution produces a testing framework that you can use to run your CHESS tests.  The project is called Alpaca (a lovely parallel and concurrency analyser), more info can be found in the presentation here.  I used this instead for executing my tests rather than the command line version of CHESS directly.

Using CHESS I managed to track down a deadlock with the ThreadPool Callback method.  I was storing the RegisteredWaitHandle returned from RegisterWaitForSingleObject and then unregistering that in the callback.  Initially this threw an exception in Alpaca.  However when I added the [ScheduledTestMethod] attribute on the test method I got a useful output specific to catching deadlock:

multithreading test framework

I then had the choice of viewing the deadlock in the Concurrency Explorer or attaching to the failed test in Visual Studio at the point of deadlock.  These options helped me to see the cause of the issue.  I fixed the deadlock by not storing the returned RegisteredWaitHandle for the reasons mentioned in the Callback – ThreadPool section in part two.

Threading Methods Not Used

There are other methods for executing logic in parallel which I didn’t implement for my test case as I didn’t think that they were suitable.  However I feel that they are worth a mention because they can be very useful in other situations:

Parallel.For – Executes an Action for each iteration in the range specified, where iterations can run in parallel.  Synchronisation of each execution is awkward however as this method is more suitable to situations where each Action is autonomous.  For that reason I decided against using it.

Parallel.ForEach – Executes an Action on each element of an IEnumerable collection where operations can run in parallel.  This method wasn’t used for the same reason mentioned above.

Parallel.Invoke – Executes an array of Actions potentially in parallel.  However as no guarantees are given about the order in which the actions are invoked, I deemed it unsuitable.

System.Timers.Timer.Elapsed – The delegate assigned to the Elapsed event on a Timer is executed on a ThreadPool thread unless specified otherwise through the SynchronizingObject property.  For the scenario in the test case we need more control over the synchronisation of each thread which is cumbersome using timers.

System.Threading.Timer – The differences between this timer and the above timer include the ability to pass object state to the callback as well as the support for higher precision intervals (up to Int64).  The callback is executed on a ThreadPool thread as above but there is no way to override that behaviour.  This method wasn’t used for the same reason the previous timer wasn’t used.

Summary

Don’t add parallelism to your application for the sake of it.  Only do so if you have a reason (e.g. you want to prevent your application UI from becoming unresponsive when executing long running tasks).

Using the Tasks is the optimal solution for adding parallelism to your application.  It separates the coordination of the execution of the synchronous code from the work that is executed on the ThreadPool.  It is better than Thread because of the fact that the thread scheduling is automatic.  It is a higher level of abstraction compared to the ThreadPool.  It is an improvement on BackgroundWorker because of its progress reporting, cancellation and nesting support.

Source Code

The source for the multithreaded application can be found on Bitbucket

References

Tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *