Multithreading with C#
Pages: 1, 2, 3, 4
Inter-Thread Synchronization
Conceptually, threads are pretty simple to understand. Practically
however, because they can inter-operate on the same data structures,
they are a cause for programmatic headaches. Take the
ThreadingExample class described at the beginning of this article --
there are many different permutations of results that may appear on
the console when the program is run. Anything from
1 2 3 4 5 1 2 3 4 5
to
1 1 2 2 3 3 4 4 5 5
or
1 2 1 2 3 3 4 5 4 5
is possible. (In reality, both programs had to be modified to count from 1 to 50 for me to demonstrate this to myself on my laptop.) The output is left to nuances of the operating system's thread scheduler. Sometimes it is necessary to make sure that only one thread can access a given set of data structures so as to make sure the data structures stay consistent -- and that is why we need inter-thread synchronization semantics.
In order to guarantee a certain behavior we have to coordinate the actions of the two threads through the use of locks. Both languages allow you to acquire a lock on any reference object -- once that lock is acquired, the program block can be assured that it is the only block that has acquired it. Also by using that lock, a program can wait until a signal comes through the variable causing the thread to wake up.
Table 2: Thread synchronization |
||
| Java | C# | Notes |
| synchronized | lock | The C# lock statement is actually syntactic sugar for using the |
Object.wait() |
Monitor.Wait( object obj ) |
There is no wait method inherent to an Object. To wait for a signal, the |
Object.notify() |
Monitor.Pulse( object obj ) |
See the notes for |
Object.notify() |
Monitor.PulseAll( object obj ) |
See the notes for |
We can modify the given example to let the variables increase in lock step in Java by first adding a variable to synchronize on and then modifying the count() method to
public static Object synchronizeVariable = "locking variable";
public static void count() {
synchronized( synchronizeVariable ) {
for( int count=1;count<=5;count++ ) {
System.out.print( count + " " );
synchronizeVariable.notifyAll();
if( count < 5 )
try {
synchronizeVariable.wait();
} catch( InterruptedException error ) {
}
}
}
}
What happens with these changes is that one thread, and one thread
only (because only one thread can grab synchronizeVariable at at
time), enters the for loop printing out 1. Next, it notifies all
threads that are waiting on synchronizeVariable (although currently
no threads are waiting) to wake up and attempt to grab the locking
variable, and then it waits on the lock variable, which releases its
hold on synchronizeVariable. The next thread can then enter the
loop, print out 1, call notifyAll() to wake up the former thread and
cause it to start attempting to grab hold of synchronizeVariable,
then put itself into a wait(), releasing the variable to allow the
former thread to grab it. This goes on until they have both printed
1 through 5 in lock step.
This can be easily translated into C# with a few syntactical changes
public static Object synchronizeVariable = "locking variable";
public static void Count() {
lock( synchronizeVariable ) {
for( int count=1;count<=5;count++ ) {
Console.WriteLine( count + " " );nbsp;
Monitor.PulseAll( synchronizeVariable );
if( count < 5 )
Monitor.Wait( synchronizeVariable );
}
}
}
Some C# Goodies
As we have come to expect from C#, the language has a few more built-in methods, classes, and functionalities that Java does not have. These may prove to be helpful to the hardcore Java threads programmer, as they are usually items that Java programmers either wish for, write, or import into their own code.

