Summary
This article describes thread basics covering the following areas:
- How to create a thread; use
System.Thread()
class and create an instance.
- How to join a thread; use
object.Join()
to join threads.
- How to suspend a thread; use
object.Sleep(<No of seconds>)
to suspend a thread.
- How to kill a thread; use
object.Abort()
to abort a thread.
- Using
Interlocked
class which uses Increment
and Decrement
methods to increment and decrement values.
Thread Synchronization is explained covering the following:
- Using Locks: allows to mark a lock on the critical section of the code (program), provide synchronization to an object and then to execute the lock in effect.
- Using Monitor: allows to decide when to enter and exit the synchronization, and it lets us wait for another area of code to become free. Monitor acts as a smart lock on a resource. When we need synchronization, we can call the
enter()
method of the monitor passing the object we want to monitor. We can explicitly choose the wait()
method of the monitor to control thread ordering. In case of using wait()
, the waiting threads will be allowed to be notified of a chance to run again if the active thread calls Pulse()
. This signals the CLR that there has been a chance in the state that might free a thread that is waiting. The CLR tracks the fact that the earlier thread asked to wait, and the thread will be guaranteed access in the order in which the waits were requested. Exit()
can be called once when the thread is finished with the monitor.
Overview
In this article, we are going to look into the basics and advance concepts about Microsoft .NET Framework based Threading support. Refer to the previous article for details about the Microsoft .NET framework.
Threading is one of the integral parts of the Microsoft .NET Framework based Common Language Runtime. Refer to the previous article on .NET Framework for more details.

Architecture of CLR
Threading support is in-built under the Common Language Runtime provided by the Microsoft .NET Framework.
Understanding Threading
- Threads are basically light weight processes responsible for multitasking within a single application.
- The base class used for threading is
System.Threading
.
- Threads are managed under the Common Language Runtime, programmers don�t have to manage any threads explicitly.
- Threads are implemented when you have situations in which you want to perform more then one task at a time.
- In case of synchronization, since you have limited amount of recourses, you may have to restrict the access to the resources to one thread at a time. In these situations, you may implement locking on the threading to over come the scenarios.
- An apartment is a logical container within a process and is used for objects that share the same thread-access requirement. Objects in the apartment can all receive method calls from any object in any thread in the apartment. And managed objects (object created within CLR) are responsible for thread safety.
- Threads can be used under situations where we must wait for an event such as user input, a read from file, or receipt of data over the network.
Working with Threads
Create a new instance of the Thread
object. The Thread
constructor accepts one parameter that is a delegate. MS CLR provides the ThreadStart
delegate class for starting the thread.
Example:
Thread myThread = new Thread( new ThreadStart(myFunc) );
To run this thread, we need the following:
Thread t1 = new Thread( new ThreadStart(Incrementer) );
To instantiate this, we need the following:
t1.Start( );
A detailed example is as below:
namespace Programming_CSharp
{
using System;
using System.Threading;
class Tester
{
static void Main( )
{
Tester t = new Tester( );
t.DoTest( );
}
public void DoTest( )
{
Thread t1 = new Thread(new ThreadStart(Incrementer) );
Thread t2 = new Thread(new ThreadStart(Decrementer) );
t1.Start( );
t2.Start( );
}
public void Incrementer( )
{
for (int i =0;i<1000;i++)
{
Console.WriteLine("Incrementer: {0}", i);
}
}
public void Decrementer( )
{
for (int i = 1000;i>=0;i--)
{
Console.WriteLine("Decrementer: {0}", i);
}
}
}
}
Output:
Incrementer: 102
Incrementer: 103
Incrementer: 104
Incrementer: 105
Incrementer: 106
Decrementer: 1000
Decrementer: 999
Decrementer: 998
Decrementer: 997
Joining Threads
Once a thread starts running and in some situation if we need to tell the thread to stop processing and wait until a second thread completes processing, we need to join the first thread to the second thread. Use the following for the same. This will join the second thread to the first one.
Example:
t2.Join( );
Suspending the Thread
In some situations, we might want to suspend a running thread.
Example:
t2..Sleep(<No of Seconds>);
Killing a Thread
Threads has to die after the execution of the process in normal situations, occasionally it is required for the programmer to kill a thread. Threads can be killed using the following:
Example:
t2.Abort();
Advanced Threading
Synchronization
In some situations, we need to synchronize the running threads so we can modify the running thread and its resources.
namespace Programming_CSharp
{
using System;
using System.Threading;
class Tester
{
private int counter = 0;
static void Main( )
{
Tester t = new Tester( );
t.DoTest( );
}
public void DoTest( )
{
Thread t1 = new Thread( new ThreadStart(Incrementer) );
t1.IsBackground=true;
t1.Name = "ThreadOne";
t1.Start( );
Console.WriteLine("Started thread {0}",
t1.Name);
Thread t2 = new Thread( new ThreadStart(Incrementer) );
t2.IsBackground=true;
t2.Name = "ThreadTwo";
t2.Start( );
Console.WriteLine("Started thread {0}", t2.Name);
t1.Join( );
t2.Join( );
Console.WriteLine("All my threads are done.");
}
public void Incrementer( )
{
try
{
while (counter < 1000)
{
int temp = counter;
temp++;
Thread.Sleep(1);
counter = temp;
Console.WriteLine("Thread {0}. Incrementer: {1}",
Thread.CurrentThread.Name, counter);
}
}
catch (ThreadInterruptedException)
{
Console.WriteLine("Thread {0} interrupted! Cleaning up...",
Thread.CurrentThread.Name);
}
finally
{
Console.WriteLine("Thread {0} Exiting. ", Thread.CurrentThread.Name);
}
}
}
}
Output:
>Started thread ThreadOne
Started thread ThreadTwo
Thread ThreadOne. Incrementer: 1
Thread ThreadOne. Incrementer: 2
Thread ThreadOne. Incrementer: 3
Thread ThreadTwo. Incrementer: 3
Thread ThreadTwo. Incrementer: 4
Thread ThreadOne. Incrementer: 4
Thread ThreadTwo. Incrementer: 5
Thread ThreadOne. Incrementer: 5
Thread ThreadTwo. Incrementer: 6
Thread ThreadOne. Incrementer: 6
Using Interlock
- MS CLR provides synchronization tools and mechanisms. This will allow programmers to place locking over running threads.
- .NET provides a special class called
Interlocked
just for the reason of locking. This consists of two methods: Increment
and Decrement
.
Example:
public void Incrementer( )
{
try
{
while (counter < 1000)
{
Interlocked.Increment(ref counter);
Thread.Sleep(1);
Console.WriteLine("Thread {0}. Incrementer: {1}",
Thread.CurrentThread.Name, counter);
}
}
}
Output (excerpts):
Started thread ThreadOne
Started thread ThreadTwo
Thread ThreadOne. Incrementer: 1
Thread ThreadTwo. Incrementer: 2
Thread ThreadOne. Incrementer: 3
Thread ThreadTwo. Incrementer: 4
Thread ThreadOne. Incrementer: 5
Thread ThreadTwo. Incrementer: 6
Thread ThreadOne. Incrementer: 7
Thread ThreadTwo. Incrementer: 8
Thread ThreadOne. Incrementer: 9
Thread ThreadTwo. Incrementer: 10
Thread ThreadOne. Incrementer: 11
Thread ThreadTwo. Incrementer: 12
Thread ThreadOne. Incrementer: 13
Thread ThreadTwo. Incrementer: 14
Thread ThreadOne. Incrementer: 15
Thread ThreadTwo. Incrementer: 16
Thread ThreadOne. Incrementer: 17
Thread ThreadTwo. Incrementer: 18
Thread ThreadOne. Incrementer: 19
Thread ThreadTwo. Incrementer: 20
Using Locks
A lock
marks a critical section of the code, and provides synchronization to an object.
Example:
public void Incrementer( )
{
try
{
while (counter < 1000)
{
lock (this)
{
int temp = counter;
temp ++;
Thread.Sleep(1);
counter = temp;
}
Console.WriteLine("Thread {0}. Incrementer: {1}",
Thread.CurrentThread.Name, counter);
}
}
Using Monitor
There are situations where the programmer need to monitor the running threads, for which we can use the following:
Monitor.Enter(this);
Race Condition and Deadlocks
There are situations when the process goes for a deadlock situation. Synchronization is a little tricky to handle in such cases.
Race Conditions
- This situation occurs when success of one program depends on uncontrolled order of execution of certain processes (two independent threads).
- We can overcome this situation by using
Join()
and using Monitor()
, Wait()
etc�
Deadlocks
In situations when one thread is dependent on another thread's completion, it is some times possible that unknowingly one thread can wait for the other to finish, so the second thread can go ahead and run the second process. In few occasions, each thread may go in a loop to wait for the next thread to complete the processing to start, where both the threads are waiting for each other to complete and none of them is actually doing any processing.