InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Minor update on my multi threading library

Here is a small update on my multi threading framework. After some background reading on how the BackgroundWorker synchronizes event invocations, I decided to implement a similar functionality into my framework as well.

UI Synchronization

The Background worker allows updates to the UI without the need to call Control.Invoke. It does this by utilizing the SynchronizationContext class (A good article on this topic can be found here on CodeProject).

This version of my multithreading framework also uses this mechanism to call the Completed, ProgressChanged and Error events. And best of all, this is agnostic to the UI framework used: It works with Windows Forms as well as Windows Presentation Foundation.

Locking inside an asynchronous method

Another issue you should know when working with my framework is this: You must never return a NestedOperation or a ParallelOperaton from within a region protected by either a Monitor (explicitly or via the lock (SyncLock in Visual Basic) keyword) or a Mutex. This is because these synchronization primitives are thread-dependent. Use a Semaphore instead.

Consider the following code:

    1 if (_ConfigurationNamingContext == null)

    2 {

    3     lock (_ConfigurationNamingContextLock)

    4     {

    5         if (_ConfigurationNamingContext == null)

    6         {

    7             using (

    8                 AsyncOperation<int, Item> operation =

    9                     ActiveDirectoryEntry.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection)

   10                 )

   11             {

   12                 yieldreturnnewNestedOperation(operation, false);

   13                 _ConfigurationNamingContext = operation.Result;

   14             }

   15         }

   16     }

   17 }

The code from line 3 through line 16 are protected by a Monitor to ensure that the code is not executed twice (which could hapen when the method is called in parellel). In line 12, a nested operation is executed, doing the hard work. But when the displayed method is resumed in line 13, the code may run on a different thread than before. This will cause the Monitor.Exit (which is implicitly called in line 16) to throw a SynchronizationLockException.

To avoid this, use a Semaphore instead.

Create a semaphore with a maximum count of 1:

    1 privatestaticSemaphore _ConfigurationNamingContextLock = newSemaphore(1, 1);

Now, use the semaphore as outlined here:

    1 if (_ConfigurationNamingContext == null)

    2 {

    3     try

    4     {

    5         _ConfigurationNamingContextLock.WaitOne();

    6 

    7         if (_ConfigurationNamingContext == null)

    8         {

    9             using (

   10                 AsyncOperation<int, Item> operation =

   11                     ActiveDirectoryEntry.GetConfigurationNamingContext(_Connection.ActiveDirectoryConnection)

   12                 )

   13             {

   14                 yieldreturnnewNestedOperation(operation, false);

   15                 _ConfigurationNamingContext = operation.Result;

   16             }

   17         }

   18     }

   19     finally

   20     {

   21         _ConfigurationNamingContextLock.Release();

   22     }

   23 }

It's not as neat as using the lock keyword, but it works :-)

Change log for version 2.5.1

  • Events are now called via a SynchronizationContext. This makes the usage of Invoke() methods unnecessary when reporting progress to Windows Forms or WPF applications.

Downloads

InfiniTec.Threading.zip (277,385 Bytes)
Source, Binaries and Documentation for version 2.5.1

Technorati:

Posted by Henning Krause on Sunday, January 28, 2007 12:00 AM, last modified on Sunday, January 28, 2007 12:00 PM
Permalink | Post RSSRSS comment feed