background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

dotNet Threading, Part I 

by Randy Charles Morin 

One of my New Year wishes for this coming year was that the standard committees would 
agree on threading classes for the C++ language. This limitation of the C++ language 
standard means that I have to rewrite my threading library each time I start a new job with 
a new company. I’ve always wished there was a standard threading library that I could use 
wherever I go.   

Fortunately, C# does not have this disadvantage. Right from the get go, the language 
inherits an entire set of threading classes from the dotNet framework. The 
“System.Threading” dotNet namespaces includes 14 utility classes, 4 exception classes, 2 
structures, 6 delegates and 3 enumerations. I’ll present most of these in this article. 

Intermediate Level 

This article is written for the intermediate and senior C# developer. Working knowledge 
of the C# programming language and dotNet framework is assumed. The article was 
written with a Beta version of VS.NET and associated documentation. Changes, although 
not anticipated, might occur before final release of VS.NET that invalidate portions of this 
article.  

Creating Threads 

Creating a thread in C# is close to trivial, but not quite. The only non-trivial thing about 
creating a thread is dotNet delegate-classes. Let me explain in few words what is a 
delegate class. The delegate is a wrapper around a code construct in the dotNet. The code 
construct could be an object instance, an instance method or a static method. Delegates 
are used when you want to pass one of the three code constructs as a parameter to 
another method. 

When creating a new thread you have to use the ThreadStart delegate class to wrap the 
instance method that will be executed in the newly created thread. The instance method 
must return void and must not have any parameters.  

void ThreadStart() 
 

To create a new thread, first create a new ThreadStart object, passing the instance method 
of the thread procedure in the constructor. The new delegate object is then passed to the 
constructor of the Thread. 

Thread thread = new Thread( 
   new ThreadStart(obj.ThreadStart)); 
 

You’ve now created a new thread, but the thread is not yet started. To start the thread, 
you call the Thread.Start instance method. 

thread.Start(); 
 

And that’s it. You have a new running thread. A complete console application that creates 
a thread and outputs a couple messages to the console window is shown in Listing 1. 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

Figure 1: Named Threads in Debugger 

Listing 1: Creating Threads 

using System; 
using System.Threading; 
 
namespace ConsoleApplication1 

   class Class1 
   { 
      static void PrintHelloFromThreadName() 
      { 
         Console.WriteLine("Hello, from thread {0}",  
            Thread.CurrentThread.Name); // {0} 
      } 
 
      public void ThreadStart() 
      { 
         PrintHelloFromThreadName(); 
      } 
 
      static void Main(string[] args) 
      { 
         Thread.CurrentThread.Name = "Main thread"; 
         Class1 obj = new Class1(); 
         Thread thread = new Thread( 
            new ThreadStart(obj.ThreadStart)); 
         thread.Name = "Forked thread"; 
         thread.Start(); 
         PrintHelloFromThreadName(); 
      } 
   } 

 

A nice feature of dotNet threads, and for that matter any dotNet object, is the ability name 
the object. If you name your threads, then the debugger will pick up those names and 
you’ll have a much easier time debugging (see Figure 1). 

The frame in the bottom left of the IDE window in Figure 1 shows all the threads in out 
C# application. I set a 
breakpoint in the 
PrintHelloFromThread
Name static method in 
Listing 1 and ran the 
application. When the 
application stops on 
the breakpoint, I called 
up the threads window 
from the menu bar, 
Debug | Window | 
Threads. As you can 
see, the Name in the 
threads window of the 
IDE is the same as the 
name given the Thread 
object in our C# code. 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

Thread Pools 

I was very impressed when I found out that the dotNet framework library included the 
“System.Threading.ThreadPool” class. I was also impressed by how easy it was to use. 
You need not create the pool of threads, nor do you have to specify how many consuming 
threads you require in the pool. The ThreadPool class handles the creation of new threads 
and the distribution of the wares to consume amongst those threads.  

You can kick off a consuming thread pool by simply 
invoking the ThreadPool.QueueUserWorkItem static 
method. 

ThreadPool.QueueUserWorkItem( 
   new WaitCallback(Consume), ware); 
 

The parameters of the QueueUserWorkItem static method 
are the WaitCallback delegate that wraps the instance 
method used in consuming your ware and the ware that 
you are passing to the method. Your consuming instance 
method must return void and take one object parameter. 
The ware that is passed to the QueueUserWorkItem 
method will be passed into your consuming instance 
method as the one object parameter. 

public void Consume(Object obj) 
 

Again, the simplicity of C# and the dotNet framework shine through. In just a few lines of 
code, I’ve recreated a multithreaded consumer-producer application (see Listing 2). 

Listing 2: Creating Thread Pools 

using System; 
using System.Threading; 
using System.Diagnostics; 
 
namespace ConsoleApplication2 

   public class Ware 
   { 
      public int id; 
      public Ware(int _id) 
      { 
         id = _id; 
      } 
   } 
 
   class Class1 
   { 
      public int QueueLength; 
 
      public Class1() 
      { 
         QueueLength = 0; 
      } 
 
      public void Produce(Ware ware) 
      { 
         ThreadPool.QueueUserWorkItem( 
            new WaitCallback(Consume), ware); 
         QueueLength++; 
      } 
 
      public void Consume(Object obj) 

Ware 
For the rest of this article I 
define a ware to be an item 
that is produced by the 
producing thread and 
consumed by a consuming 
thread in the consumer-
producer design pattern. 
This is a very narrow 
definition of the word, but 
one that suits this article. 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

      { 
         Console.WriteLine("Thread {0} consumes {1}",  
            Thread.CurrentThread.GetHashCode(), //{0} 
            ((Ware)obj).id); //{1} 
         Thread.Sleep(100); 
         QueueLength--; 
      } 
 
      public static void Main(String[] args) 
      { 
         Class1 obj = new Class1(); 
         for (int i = 0; i < 1000; i++) 
         { 
            obj.Produce(new Ware(i)); 
         } 
         Console.WriteLine("Thread {0}",  
            Thread.CurrentThread.GetHashCode() ); //{0} 
         while (obj.QueueLength != 0) 
         { 
            Thread.Sleep(1000); 
         } 
      } 
   } 

 

I added the line Thread.Sleep(100) in the Consume method to simulate the processing that 
a consumer would normally have performed on the ware. If I didn’t include this Sleep’ing, 
then one consumer thread could have handled all 100 wares. The additional Sleep’ing 
forces the dotNet framework to create additional threads and more accurately portrays the 
features of the ThreadPool class. 

Synchronization Objects 

The previous code contains some rather inefficient coding when the main thread cleans up. 
I repeatedly test the queue length every second until the queue length reaches zero. This 
may mean that the process will continue executing for up to a full second after the queues 
are finally drained. Wow! I can’t have that. 

OK! Maybe that’s not a good reason to change the code, but it is a convenient excuse for 
me to introduce you to the System.Threading.ManualResetEvent class. Using a 
ManualResetEvent object, I could trigger the main thread to complete as soon as the last 
ware was consumed. I’ll do this by creating two new instance data members, a bool 
WaitForComplete to tell us when the main thread is waiting to exit and a 
ManualResetEvent Event object that will signal the main thread to exit (see Listing 3). 

Listing 3: Using Events 

private bool WaitForComplete; 
private ManualResetEvent Event; 
 
public void Wait() 

   if (QueueLength == 0) 
   { 
      return; 
   } 
   Event = new ManualResetEvent(false); 
   WaitForComplete = true; 
   Event.WaitOne(); 
}    
 
public void Consume(Object obj) 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

   Console.WriteLine("Thread {0} consumes {1}",  
      Thread.CurrentThread.GetHashCode(), //{0} 
      ((Ware)obj).id); //{1} 
   Thread.Sleep(100); 
   QueueLength--; 
   if (WaitForComplete) 
   { 
      if (QueueLength == 0) 
      { 
         Event.Set(); 
      } 
   }; 

 

When the consuming thread finishes consuming a ware and detects that the 
WaitForComplete is true, it will trigger the Event when the queue length is zero. Instead 
of calling the while block when it wants to exit, the main thread calls the Wait instance 
method. This method sets the WaitForComplete flag and waits on the Event object. 

Let me test your threading prowess. The previous listing contained a race condition. Can 
you find it? Take a minute or two before continuing. Tic! Tic! Tic! 

Race Condition 

A race condition is a bug caused by an incorrect assumption as to the timing of two 
events, that is, that one event would always occur before the other. 

The race condition occurs when the system shuts down. If the main thread is swapped out 
in the Wait instance method between testing if the queue length is zero and setting the 
WaitForComplete flag to true and then the last consuming thread exits the Consume 
instance method while the main thread is in this state, the event will never be triggered. I 
ran the code a few hundred times and was never able to trigger the condition. You can’t 
reproduce it because the main thread should be waiting on the event object well before the 
last consuming-thread exits.  

Monitor and Lock 

I could have arranged the code otherwise to prevent this race condition, but now I’ve 
created another opportunity to introduce you to the System.Threading.Monitor class and 
the lock C# construct. The monitor design pattern is most familiar to Java developers. In 
Java, the synchronized keyword allowed the developer to create quick critical sections 
within their code. The Java construct was often called a monitor. 

The dotNet framework presents a similar class called the Monitor that implements 
traditional wait and signal methods called Wait and Pulse. The C# compiler uses this 
Monitor class to implement a language construct called a lock. The lock is established on 
an object and while the lock is established, nobody else can acquire the lock and must wait 
till the lock is freed. I used this lock construct to prevent our previous race condition (see 
Listing 4). 

Listing 4: Using Monitors 

public void Wait() 

   lock (this) 
   { 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

      if (QueueLength == 0) 
      { 
         return; 
      } 
      Event = new ManualResetEvent(false); 
      WaitForComplete = true; 
   } 
   Event.WaitOne(); 
}    
 
public void Consume(Object obj) 

   Console.WriteLine("Thread {0} consumes {1}",  
      Thread.CurrentThread.GetHashCode(), //{0} 
      ((Ware)obj).id); //{1} 
   Thread.Sleep(100); 
   lock (this) 
   { 
      QueueLength--; 
      if (!WaitForComplete) 
      { 
         return; 
      } 
   } 
   if (QueueLength == 0) 
   { 
      Event.Set(); 
   }; 

 

Preventing the concurrent setting and testing of the queue length and WaitForComplete 
flag by two different threads removes the race condition. The lock ensures that the setting 
and testing of these two variables is essentially atomic. 

Join 

Before dotNet, I was often asked questions about how to wait for a Win32 thread to exit. 
The solution was to acquire a handle to the thread and wait on the handle. Or 
alternatively, you could setup an event that was triggered at the end of the thread and wait 
on that event. dotNet provides us with a simpler method of doing the same. If you call the 
Thread.Join instance method, then the current thread will wait until the thread represented 
by the Thread object is terminated (see Listing 5).  

Listing 5: Using Join 

using System; 
using System.Threading; 
using System.Diagnostics; 
 
namespace ConsoleApplication7 

   class Class1 
   { 
      public void Pump() 
      { 
         for (int i=0;i<100;i++) 
         { 
            Console.WriteLine("Value {0}", i); 
            Thread.Sleep(1); 
         } 
      } 
 
      static void Main(string[] args) 
      { 
         Class1 obj = new Class1(); 
         Thread pump = new Thread( 
            new ThreadStart(obj.Pump)); 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

         pump.Start(); 
         Thread.Sleep(500); // force the other thread  
                   // thru a couple iterations 
         pump.Join(); // wait until the thread is  
                   // completed 
         Console.WriteLine("Goodbye"); 
      } 
   } 

 

In this previous listing, the main thread creates a new thread (pump), then waits for the 
thread to complete by calling the pump.Join instance method. If you run the previous 
code, as is, then the output will be the numbers 0 to 99 and finally the word Goodbye. If 
you remove the call to pump.Join, then the Goodbye message may be printed before the 
last number. I chose to put the main thread to sleep for half a second as this displayed the 
Goodbye message in the middle of the stream of numbers (when pump.Join was removed).  

AutoResetEvent & Timer 

Early in the article, I introduced you to the ManualResetEvent class. This class allowed 
you to set and reset (signal and unsignal) the event by calling the Set and Reset instance 
methods. The System.Threading.AutoResetEvent class is very similar to the 
ManualResetEvent class, but when a thread waiting on the event is signaled, the one 
thread is released and the event is returned to the unsignaled state. This removes the 
necessity to reset the signal after a thread is signaled. 

Another great class in the System.Threading namespace is the Timer class. This class 
allows you to signal an event at a particular interval in time in the future. The Timer class 
is implemented using a delegate callback instance method. When the Timer is signaled, the 
class calls the instance method that you specified in the constructor of the Timer object. 
The Timer callback can also receive a parameter object passed in the call to the Timer 
constructor. Presented in Listing 6 is a small sample using the AutoResetEvent and Timer 
classes. 

Listing 6: AutoResetEvent and Timer Class 

using System; 
using System.Threading; 
 
namespace ConsoleApplication8 

   class Class1 
   { 
      public void TimerCallback(Object obj) 
      { 
         Console.WriteLine("Timer triggered"); 
         ((AutoResetEvent)obj).Set(); 
         Thread.Sleep(1000); 
         ((AutoResetEvent)obj).Set(); 
      } 
 
      static void Main(string[] args) 
      { 
         Class1 obj = new Class1(); 
         AutoResetEvent ev =  
            new AutoResetEvent(false); 
         Timer timer = new Timer( 
            new TimerCallback(obj.TimerCallback),  
               ev, 1000, 0); 
         ev.WaitOne(); 
         Console.WriteLine("Event Fired"); 

background image

www.kbcafe.com

 

 

January 21, 2002 

 

Copyright 2002-2003 Randy Charles Morin 

         ev.WaitOne(); 
         Console.WriteLine("Event Fired"); 
      } 
   } 

 

Note that the Timer callback instance method is wrapped in a TimerCallback delegate 
object. The main thread will create an AutoResetEvent object and a Timer object. The 
main thread then waits on the event object. The TimerCallback instance method is called 
after one second, triggering the event object. Because the event object is automatically 
reset, when the main thread attempts to wait on the event again, the thread yields until the 
event is signaled a second time. The TimerCallback instance method waits another second 
and then signals the event a second time, releasing the main thread. 

More 

In the second part of this article, I will complete my discussion of the synchronization 
objects and will discuss thread local storage, COM interoperability and thread states. 

About the About 

Randy Charles Morin is the Lead Architect of SportMarkets Development from Toronto, 
Ontario, Canada and lives with his wife and two kids in Brampton, Ontario. He is the 
author of the 

www.kbcafe.com

 website, author of Wiley’s Programming Windows 

Services book and co-author of many other programming books and articles.