Understanding Threads

November 2009

Introduction

A process is created by the operating system to manage execution of an instance of an application. In the process, the path of execution of the program steps, comprising the application, is called the thread of execution, or simply thread.

In a simple application the steps are performed sequentially in a single thread. Often, applications have tasks that can be performed simultaneously:
  • Retrieval or insertion of information into a database or communicating over a network;
  • Mathematical computations that involve resolving conditions at a multitude of nodes; and
  • Background tasks like printing.
Processes provide for the creation of additional threads to enable the effective utilization of processor resources. For example, a thread can be created to perform time consuming low priority tasks enabling other threads to perform short and high priority tasks. As such, processing of the short and high priority tasks is not deferred while the time consuming task is completed. In multiprocessor environments the threads are simply assigned to different processors. In a single processor environment, the each thread is processed for a short period of time, called a “time slice”.
Processor time is allocated to multiple threads in turnThe diagram illustrates the case. The Short and High Priority tasks are completed before the long Background task even though the Background task started first.
In actuality, the time slices are small and the threads appear to be executing simultaneously. When the operating system switches from processing one thread to another it saves the thread’s context, data associated with the computation including registers, and loads the context of the new thread.

The Thread Class, System.Threading namespace, models a thread including creation and use. The following code illustrates creation and control of a thread. The Thread constructor calls for a delegate representing the method that executes when the thread starts. Consider the following code:

class Program
{
  static void Main(string[] args)
  {
    Console.WriteLine("Main Thread Starting");
    ThreadStart ts = new ThreadStart(TheClass.TheMethod);
    Thread t = new Thread(ts);
    t.Start();
    Console.WriteLine("Child Thread State: {0} ", t.ThreadState);
    Thread.Sleep(500);
    Console.WriteLine("Child Thread State: {0} ", t.ThreadState);
    Console.WriteLine("Main Thread Ending");
    Console.ReadLine();
  }
}
class TheClass
{
  public static void TheMethod()
  {
    Console.WriteLine("\t\t\t\tChild Thread Starting");
    Console.WriteLine("\t\t\t\tChild Thread State: {0} ",
          Thread.CurrentThread.ThreadState);
    Console.WriteLine("\t\t\t\tChild Thread Ending");
  }
}
TheMethod in TheClass is the procedure that will be executed by the thread. The delegate given to the Thread constructor should target TheMethod. The ThreadStart delegate is declared as
public delegate void ThreadStart()
Note that delegate is declared with a void return type is parameter less. (An overload provides for passing data to a thread.).

The delegate is passed to the Thread constructor. Note that C# will select the appropriate constructor and thus a thread targeting TheMethod is also created with the statement:
Thread t = new Thread(TheClass.TheMethod);
The thread starts when the Start method is called.

The ThreadState property get a value representing the state of the thread. Possible states include the following:

Running
The thread is running
Stopped
The thread has stopped
WaitSleepJoin
The thread is blocked, sleeping or waiting for a synchronizing action
The ThreadState property should never be used for synchronizing thread activity!
Note that the CurrentThread property gets the currently running thread. Thus the statement
Thread.CurrentThread.ThreadState
in TheMethod returns the state of the thread which is currently stepping through the program statements in TheMethod. The program’s output is as follows:
Main Thread Starting
Child Thread State: Running
  
  Child Thread Starting
Child Thread State: Running
Child Thread Ending
Child Thread State: Stopped
Main Thread Ending
  

Joining

What happens when a child thread is joined?

The calling thread waits for the called thread to finish. The calling thread is said to be "blocked." Consider the following code.
static void Main(string[] args) {
  Thread child = new Thread
    (delegate(){ Thread.Sleep(3000); Console.WriteLine("child ending"); });
  child.Start();
  Console.WriteLine("Main ending");
  Console.ReadLine();
}
The Main thread completes before the child thread. A child thread is created and started. While the chld thread is sleeping the Main program thread executes to the end and "Main ending" is dispalyed. After a few seconds the child thread wakes and finishes, displaying "child ending."

Consider the following modification in which the calling thread, executing the Main routine, is blocked until the child thread finishes. The Thread.Join method enables the block..
static void Main(string[] args) {
  Thread child = new Thread
    (delegate(){ Thread.Sleep(3000); Console.WriteLine("child ending"); });
  child.Start();
  child.Join();
  Console.WriteLine("Main ending");
  Console.ReadLine();
}
The Mian thread joins in the wait for the child thread to end Again the child thread is created and started. However this time the thread in which the Main is executiong is blocked with the call to the child thread's Join method: child.Join(). When the child thread finishes, indicated by the "child ending" display, the thread executing Main is unblocked and resumes executing. The "Main ending" is finally displayed.

When the Join is called, the thread in which the call is made, in this case the Main thread, joins in the wait for the called thread to finish before continuing execution. .

Thread Safety and Locking

Thread safe implies that the code is determinate in all multi-threading situations. A method that is thread safe is said to be reentrant.

The method UnsafeProc in AClass show below is an example of a method that is not thread safe. The class has two fields. A DateTime field is set to the time of instantiation. A critical value is passed every time the UnsafeProc is called.

The UnsafeProc displays the thread, time and critical value upon entry. Processing is suspended by calling Thread.Sleep. When the thread wakes the thread, time and critical value are again displayed.

class AClass
{
  DateTime baseTime;
  int criticalValue;

  public AClass()
  {
    baseTime = DateTime.Now;
  }

  public void UnsafeProc(int i)
  {
    criticalValue = i;
    DateTime now = DateTime.Now;
    TimeSpan startingTime = now - baseTime;
    Console.WriteLine("Thread {0} starting at {1} with critical value {2}",
      Thread.CurrentThread.GetHashCode(), startingTime.Milliseconds,
      criticalValue);
    Thread.Sleep(200);
    now = DateTime.Now;
    TimeSpan endingTime = now - baseTime;
    Console.WriteLine("Thread {0} ending at {1} with critical value {2}",
      Thread.CurrentThread.GetHashCode(), endingTime.Milliseconds,
      criticalValue);
  }
}
AClass is used in Program, shown below.
class Program
{
  private static AClass aClass = new AClass();

  static void Main(string[] args)
  {
    Console.WriteLine("Main starting on Thread: {0}",
    Thread.CurrentThread.GetHashCode());
    for (int i = 0; i < 3; i++)
    {
      Thread t = new Thread(ThreadProc);
      t.Start(I + 100);
    }
    Console.ReadLine();
  }

  private static void ThreadProc(object state)
  {
    aClass.UnsafeProc((int)state);
  }
}
The Main programs creates three threads with the statement
Thread t = new Thread(ThreadProc);
The constructor targets the method ThreadProc. Note that the C# compiler automatically selects the appropriate delegate, in this case ParameterizedThreadStart. The parameter is passed to the thread in the Start method.

The TheadProc method calls the UnsafeProc .

The program’s output is shown below
Main starting on Thread: 9
Thread 10 starting at 544 with critical value 100
Thread 11 starting at 547 with critical value 101
Thread 12 starting at 549 with critical value 102
Thread 10 ending at 745 with critical value 102
Thread 11 ending at 747 with critical value 102
Thread 12 ending at 749 with critical value 102
Note that although each thread starts the computation with the critical value passed value they all end with the same value, the value assigned by the last thread to set the value. Imagine the havoc that would result in a banking environment where the threads represent activity of different ATMs and just before the procedure ends the critical value is used to adjust the account’s balance.

Monitor objects control access to a section of code. For example, when the Monitor’s Enter method is invoked,
Monitor.Enter(m_AClass);
the Monitor acquires an exclusive lock on the object, in this case m_AClass. Other threads that call an Enter on the object will block until an Exit call is invoked.
Monitor.Exit(m_AClass);
A Try…Finally block should always be employed to ensure that an Exit is invoked releasing the Monitors lock on the object:
Monitor.Enter(anObject);
Try
{
  //some code ...
}
Finally
{
  Monitor.Exit(anObject);
}
Monitor objects can be used to create thread safe method. Consider the SafeProc method:
public void SafeProc(int i)
{
  TimeSpan startTime;
  TimeSpan enterTime;
  TimeSpan exitTime;
  DateTime now;
  AClass m_AClass = this;

  now = DateTime.Now;
  startTime = now - m_AClass.baseTime;
  Console.WriteLine("Safe Thread {0} started at {1}
    with critical value {2}", Thread.CurrentThread.GetHashCode(),
    startTime.Milliseconds, m_AClass.criticalValue);
  Thread.Sleep(20);
  Monitor.Enter(m_AClass);
  try
  {
    now = DateTime.Now;
    enterTime = now - m_AClass.baseTime;
    Console.WriteLine("Safe Thread {0} entered at {1}
      with critical value {2}", Thread.CurrentThread.GetHashCode(),
      enterTime.Milliseconds, m_AClass.criticalValue);
    Thread.Sleep(200);
    m_AClass.criticalValue = i;
    now = DateTime.Now;
    exitTime = now - m_AClass.baseTime;
    Console.WriteLine("Safe Thread {0} exited at {1}
      with critical value {2}", Thread.CurrentThread.GetHashCode(),
      exitTime.Milliseconds, m_AClass.criticalValue);
  }
  finally
  {
    Monitor.Exit(m_AClass);
  }
}
Major elements of the methods execution are as follows.

A reference, m_AClass, to the instance of the AClass object is created; and the thread’s hash code, elapsed time and the critical value are displayed.

The Monitor.Enter method is invoked to acquire an exclusive lock on the instance of AClass.

If another thread has invoked an Enter on the object the thread will block until the other thread releases the lock with an Exit call. If no other thread has a lock, execution continues.

The thread’s hash code, elapsed time and the critical value are displayed.

The thread is put to sleep.

When the tread awakens, the critical value is assigned the value passed to the thread’s targeted method.

Again, the thread’s hash code, elapsed time and the critical value are displayed. Finally, the Monitor.Exit method is invoked and the thread ends.

When the SafeProc is targeted, the program’s output is as follows:
Main starting on Thread: 10
Safe Thread 11 started at 50 with critical value 0
Safe Thread 3 started at 52 with critical value 0
Safe Thread 12 started at 54 with critical value 0
Safe Thread 11 entered at 71 with critical value 0
Safe Thread 11 exited at 271 with critical value 100
Safe Thread 3 entered at 271 with critical value 100
Safe Thread 3 exited at 471 with critical value 101
Safe Thread 12 entered at 471 with critical value 101
Safe Thread 12 exited at 671 with critical value 102
Note that the threads all start quickly and that the thread 3 blocks until thread 11 is completed. Note also thread at when the Enter on three is invoked the critical value is 100. Before thread three ends it changes the critical value to 101. The execution on thread 12 is similar. The following diagram illustrates the concept.
 threads wait until lock resource is released
The Lock keyword acquires an exclusive lock on an object, executes a block of code and releases the lock.
Lock ( SomeObject )
{
// SomeCode
}
The Lock keyword is syntactically equivalent to the following:
Monitor.Enter ( SomeObject );
{
// SomeCode
}
Finally
{
Monitor.Exit( SomeObject );
}
The material presented has been gleaned from Microsoft documentation. Please contact me regarding any thoughts you have regarding Understanding Threads. I will append and attribute your comments.