AddThis Social Bookmark Button

Print

Multithreading with C#
Pages: 1, 2, 3, 4

Enter/TryEnter/Exit


P2P and Web Services Speaker

Related Tutorials:



Building Web Services with .NET -- One of the key motivations behind .NET is the move towards web services. This tutorial provides an overview of the rich support in the Microsoft .NET Framework for building web services and networked applications.

Microsoft .NET and the Common Language Runtime -- This tutorial provides an overview of the .NET Framework, then delves into the Common Language Runtime and what it offers to developers building & deploying modern applications.


To obtain a lock on a variable in Java, you just need to surround the code with the synchronized keyword specifying the object that the program should attempt to acquire a lock on. Once a thread has started to execute code in the synchronized block, it is guaranteed to have already received a monitor lock on the object being synchronized on -- likewise, once a thread has left the block, it will have released the lock on the object. We have seen that C# has a similar construct called lock that operates on reference types. In addition to the lock construct, C# has provided access to its internal methods to acquire and release locks: Monitor.Enter( object obj )and Monitor.Exit( object obj ). Using these methods can buy a programmer the same benefits as using the lock construct, but it can also provide more elaborate locking abilities, such as being able to lock variables in one method and have them released at different times or different points in the code, depending on the code path.

A call to System.Threading.Monitor.Enter on the object to synchronize on will cause C# to either obtain the lock on the object, or block if another thread is currently holding lock (it will not block if a Thread already holding an object's lock attempts to acquire it again -- however, a programmer should ensure that a lock is released the same amount of times it is acquired). It should be noted that calling Interrupt() on a Thread which is blocking on an Enter to a lock will cause the method to unblock and throw a System.Threading.ThreadIntrerruptedException. Releasing a lock is easily done with the Monitor.Exit method -- this method will throw a System.Threading.SynchronizationLockException if it does not already own the lock on the object it is attempting to release.

What Java is lacking (and what C# has) is the ability to attempt to enter a synchronized code segment without blocking on the acquisition of the lock. C#'s Monitor class not only contains the Enter method, which blocks until it can be assured that it will be the only thread in the block, but it also contains a TryEnter method, which will either obtain a lock (optionally blocking for a given amount of time) or return with a return value of false, signifying that it cannot get a lock on the object.

Atomic Operations

As more syntactic sugar, the System.Threading.Interlocked class provides the ability for a program to synchronize access to variables that are shared amongst Threads. C# manages this by abstracting a few basic operations into "atomic" or non-splittable operations. To explain the problem that is being solved, take the following Java code block:

 public static int x = 1;

 public static void increment() {
   x = x + 1;

 }

If we have increment() called by two different threads simultaneously, x could be equal to either 2 or 3 at the end. This can happen with code from both threads interweaving with each other while looking up the variable of x and incrementing it before setting x again; both threads could read x as 1 before any thread has the chance to increment it and set x with the new value.

In both languages we can synchronize access to the x variable so that the threads can stay out of each other's way, but C# provides a cleaner solution through the use of the Interlocked class. Interlocked has a few methods such as Increment( ref int location ) and Decrement( ref int location ) that can take an integer, increment it, and return its new value all in one uninterruptable step, instead of explicitly creating a separate object that the code can synchronize on, such as this example in Java:

 public static Object locker = ...
 public static int x = 1;

 public static void increment() {
   synchronized( locker ) {
     x = x + 1;

   }

 }

C#'s Interlocked class can do the same operation with

 public static int x = 1;

 public static void Increment() {
   Interlocked.Increment( ref x );

 }

Interlocked also contains a method named Exchange, which allows for the setting of a variable from another variable in a single step.

ThreadPools

A lot of threaded applications create threads that spend a majority of their time in a waiting state waiting for a certain condition (key press, new I/O input, etc.). C# provides a single System.Threading.ThreadPool object to help with this problem -- using the ThreadPool and an event-based programming paradigm, the program can register a System.Threading.WaitHandle object (WaitHandles are C#'s object model for the wait and notify paradigm of coding) and a System.Threading.WaitOrTimerCallback delegate. Instead of having a full-fledged Thread waiting for the WaitHandle to release, the ThreadPool will monitor all the WaitHandles registered with it and then call the appropriate WaitOrTimerCallback delegate method when the WaitHandle has been released.

In this article we have seen the abstractions that C# provides for threaded and parallel operations. Most are similar to Java -- C# provides a Thread object that can run a provided method while also providing ways to synchronize access to certain blocks of code. As usual, C# also provides a few more syntactic sugars (or in some cases, exposes the underlying synchronization base) that Java programmers may find useful. By this point, we have covered most of what Java programmers need to get started in writing trivial to intermediate applications; we have focused on C# syntax, I/O, and now, threading. All that is really left will be covered in the next article: graphical applications.

Raffi Krikorian makes a career of hacking everything and anything. Professionally, he is the founding partner at Synthesis Studios: a technological design and consulting firm that orchestrates his disjointed train of thought.


Return to ONJava.com.