ParallelWork is an open source free helper class that lets you run multiple work in parallel threads, get success, failure and progress update on the WPF UI thread, wait for work to complete, abort all work (in case of shutdown), queue work to run after certain time, chain parallel work one after an

Introduction

ParallelWork is an open source free helper class that lets you run multiple work in parallel threads, get success, failure and progress update on the WPF UI thread, wait for work to complete, abort all work (in case of shutdown), queue work to run after certain time, chain parallel work one after another. It’s more convenient than using .NET’s BackgroundWorker because you don’t have to declare one component per work, nor do you need to declare event handlers to receive notification and carry additional data through private variables. You can safely pass objects produced from different thread to the success callback. Moreover, you can wait for work to complete before you do certain operation and you can abort all parallel work while they are in-flight. If you are building highly responsive WPF UI where you have to carry out multiple job in parallel yet want full control over those parallel jobs completion and cancellation, then the ParallelWork library is the right solution for you.

I am using the ParallelWork library in my PlantUmlEditor project, which is a free open source UML editor built on WPF. You can see some realistic use of the ParallelWork library there. Moreover, the test project comes with 400 lines of Behavior Driven Development flavored tests, that confirms it really does what it says it does.

Source code

The source code of the library is part of the “Utilities” project in PlantUmlEditor source code hosted at Google Code.

How to use it

The library comes in two flavours, one is the ParallelWork static class, which has a collection of static methods that you can call. Another is the Start class, which is a fluent wrapper over the ParallelWork class to make it more readable and aesthetically pleasing code.

ParallelWork allows you to start work immediately on separate thread or you can queue a work to start after some duration. You can start an immediate work in a new thread using the following methods:

There are several overloads of these functions to have a exception callback for handling exceptions or get progress update from background thread while work is in progress. For example, I use it in my PlantUmlEditor to perform background update of the application.

The above code shows you how to get exception callbacks on the UI thread so that you can take necessary actions on the UI. Moreover, it shows how you can chain two parallel works to happen one after another.

Sometimes you want to do some parallel work when user does some activity on the UI. For example, you might want to save file in an editor while user is typing every 10 second. In such case, you need to make sure you don’t start another parallel work every 10 seconds while a work is already queued. You need to make sure you start a new work only when there’s no other background work going on. Here’s how you can do it:

If you want to shutdown your application and want to make sure no parallel work is going on, then you can call the StopAll() method.

ParallelWork.StopAll();

If you want to wait for parallel works to complete without a timeout, then you can call the WaitForAllWork(TimeSpan timeout). It will block the current thread until the all parallel work completes or the timeout period elapses.

result = ParallelWork.WaitForAllWork(TimeSpan.FromSeconds(1));

The result is true, if all parallel work completed. If it’s false, then the timeout period elapsed and all parallel work did not complete.

How does it work?

Let’s see how the StartNow method works, which is responsible for queuing work in separate thread and firing success, failure and progress update callbacks on the UI thread using Dispatcher.

First it takes the current dispatcher from the caller thread. Since the caller thread is the UI thread, as you are calling ParallelWork.StartNow(), from UI thread, the Dispatcher reference resolves the UI thread. The reference is stored in a Weak reference so that the reference to the Dispatcher is not kept alive until all threads have a chance to get called and release the reference. This is the classic “closure” problem you mostly hear in the javascript world.

Then it creates a new thread and resets a ManualWaitEvent which is signaled when all threads are completed. It’s used in WaitForAllWork function to wait until all threads have completed their work.

In the thread callback, it receives the Dispatcher reference and then fires the success, failure and progress update callbacks.

The other sophisticated function is the StartNowAfter, which queues a work to happen in separate thread after some duration.

The other functions like StopAll, WaitForWork are straightforward. Nothing worth mentioning here.

Unit testing the library

There’s a xUnit test suite which tests the ParallelWork class using BDD approach. I find BDD approach better than traditional TDD approach where a single method tests a single fact only. I prefer testing a method against its overall behavior, not using a fraction of its behavior, which is a ‘fact’.

Unit testing multithreaded libraries is complex. It’s even more complicated when WPF is involved and we are using Dispatcher which is not a simple thing to test. Hope the following tests will give you some ideas on how to write tests that confirms multithreaded behavior of your components.

Here it shows that inside a parallel thread, you can create a dictionary and that’s returned to the callback and you can access dictionary and make changes to it from the callback thread.

This test shows you some of the pains of unit testing WPF Dispatcher. In order to make Dispatcher.BeginInvoke to work in a unit test, without having a WPF UI running, you have to first create a DispatcherFrame, and then call Dispatcher.PushFrame(frame) to execute the BeginInvoke calls. The problem and the solution is explained in great details in this blog post. The function WaitForWorkDoneAndFireCallback does the job of running Dispatcher and making it process the BeginInvoke calls.

Now let’s test a slightly more complicated behavior. We want to ensure the StartNowAfter does the job after the specified time, not before that and the job is carried in a separate thread, without blocking caller. The following test ensures the behavior:

The above test confirms that the StartNowAfter does not start the work before the expected time and the work completes within the expected time.

Next is to ensure the StopAllWork really stops all parallel running threads. The following test starts two parallel work and while the work is in-progress, it calls StopAllWork and ensures the threads abort and do not complete their job.

If you look at the Debug output, you will notice ThreadAbortException happening, confirming the threads really abort. Calling StopAllWork in real life scenario is dangerous. Since it might leave resources open and cause severe memory leak and resource contention. Use it with care.

Now let’s look at the most complicated test which ensure StartNow does what it’s supposed to do. I have been saving this for the last since the test is really long and complicated. There’s a lot of expectation from this function. It should execute the work in parallel thread without blocking the caller thread. It should fire the success, failure and progress update callbacks on the UI thread and so on. The following test ensures the StartNow works as expected and ensure all the major side-effects are also tested.

Comments and Discussions

Very nice, we actually do something similar to this at work,. just one question. The way we do it at work is to use have Started/Completed events raised per work item, that way in my unit tests we can wait for a waithandle to be set in the Completed before we assert about background data.

How does this library cope with that, and could you show a demo Unit test, with waits and several background things happening.

Sacha Barber

Microsoft Visual C# MVP 2008/2009

Codeproject MVP 2008/2009

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

Hi Sacha,
The complete callback fires only when the background thread has completed its work. So, you can write the code that expects background work to be completed in the complete callback. The callback is also fired on the UI thread so that you can update UI from data that's produced on the background thread.

I have shown two unit tests there. One shows passing data from background thread to UI thread complete callback. Another one shows starting two tasks in parallel and measuring when they complete. Do they address the problem you are describing?