C# is a programming language based on object oriented programming concepts and like other programming languages like Java, It supports multithreading, so this article provides you details of multithreading in C# with console application examples.

Multithreading is the ability of an operating system to execute the different parts of the program, called threads, simultaneously. The program has to be designed well so that the different threads do not interfere with each other. This concept helps to create scalable applications because you can add threads as and when needed.

A multithread program contains two or more parts that can run concurrently. Each part of such a program is called thread, and each thread defines a separate path of execution. Thus, multithreading is a specialized form of multitasking.

How does multithreading work?

.NET has been designed from the start to support multi-threaded operation. There are two main ways of multi-threading which .NET encourages: starting your own threads with ThreadStart delegates, and using the ThreadPool class either directly (using ThreadPool.QueueUserWorkItem) or indirectly using asynchronous methods (such as Stream.BeginRead, or calling BeginInvoke on any delegate).

In general, you should create a new thread "manually" for long-running tasks, and use the thread pool only for brief jobs. The thread pool can only run so many jobs at once, and some framework classes use it internally, so you don't want to block it with a lot of tasks which need to block for other things. The examples in this article mostly use manual thread creation. On the other hand, for short-running tasks, particularly those created often, the thread pool is an excellent choice.

To use multithreading we have to use the Threading namespace which is included in System. The System.Threading namespace includes everything we need for multithreading.  This namespace consists of a number of classes.

System.Threading.Thread is the main class for creating threads and controlling them.

The Thread class has a number of methods. A few interesting methods are shown below:

  • Start(): starts the execution of the thread.
  • Suspend(): suspends the thread, if the thread is already suspended, nothing happens.
  • Resume(): resumes a thread that has been suspended.
  • Interrupt(): interrupts a thread that is in the wait, sleep or join the stage.
  • Join (): blocks a calling thread until the thread terminates.
  • sleep(int x): suspends the thread for a specified amount of time (in milliseconds).
  • Abort(): Begins the process of terminating the thread. Once the thread terminates, it cannot be ·restarted by calling the function Start() again.
  • Yield(): is used to yield the execution of current thread to another thread.

This class also has a number of interesting properties as shown below:

  • IsAlive: if true, signifies that thread has been started and has not yet been terminated or aborted
  • Name: gets/sets the name of the thread
  • priority: gets/sets the scheduling priority of a thread
  • ThreadState: gets a value containing the state of the current thread
  • IsBackground: is used to get or set value whether current thread is in background or not. 
  • ManagedThreadId: is used to get unique id for the current managed thread.

Let's take a look at a simple threading example:

using System;
using System.Threading;

namespace MultiThreadingInCSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadStart job = new ThreadStart(ThreadProcess);
            Thread thread = new Thread(job);
            thread.Start();

            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Main thread: {0}", i);
                Thread.Sleep(1000);
            }
        }
        static void ThreadProcess()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("ThreadProcess thread: {0}", i);
                Thread.Sleep(500);
            }
        }
    }

}

Output:

Main thread: 0
ThreadProcess thread: 0
ThreadProcess thread: 1
Main thread: 1
ThreadProcess thread: 2
ThreadProcess thread: 3
ThreadProcess thread: 4
Main thread: 2
ThreadProcess thread: 5
Main thread: 3
ThreadProcess thread: 6
ThreadProcess thread: 7
Main thread: 4
ThreadProcess thread: 8
ThreadProcess thread: 9

multithreading-in-c-sharp-with-example-min.png

The code creates a new thread which runs the ThreadProcess Job method, and starts it. That thread counts from 0 to 9 fairly fast (about twice a second) while the main thread counts from 0 to 4 fairly slowly (about once a second). The way they count at different speeds is by each of them including a call to Thread.Sleep, which just makes the current thread sleep (do nothing) for the specified period of time. Between each count in the main thread we sleep for 1000ms, and between each count in the other thread we sleep for 500ms.

One important thing to note here is that although the above is very regular, that's by chance. There's nothing to stop the first "Other thread" line coming first, or the pattern being slightly off - Thread.Sleep is always going to be somewhat approximate, and there's no guarantee that the sleeping thread will immediately start running as soon as the sleep finishes.

So, to create a thread, we need to instantiate the Thread class, passing a ThreadStart delegate (System.Threading.ThreadStart) in its constructor. This delegate contains the method where the thread will begin execution when started. The Start() method of the Thread class then starts the execution of a new thread. Let us understand the concept with a small example.

using System;
using System.Threading;

public class Program {
 public static void Seconday_Thread() {
     Console.WriteLine("Secondary thread created");
	 Thread current_thread = Thread.CurrentThread;
	 Console.WriteLine("The details of the thread are : ");
     Console.WriteLine("Thread Name : " + current_thread.Name) ;
     Console.WriteLine("Thread State: " + current_thread.ThreadState.ToString() );
     Console.WriteLine("Thread priority level:" + current_thread.Priority.ToString());
     Console.WriteLine("Secondary thread terminated");
 }

 public static void Main()
 {
   ThreadStart thr_start_func = new ThreadStart(Seconday_Thread);
   Console.WriteLine("creating the Secondary thread");
   Thread sThread = new Thread(thr_start_func);
   sThread.Name = "Seconday_thread";
   sThread.Start(); //starting the thread
 }

}

In this example, we are creating a new thread called sThread, which when started executes the function called Seconday_Thread(). Note the use of the delegate called ThreadStart that contains the address of the function that needs to be executed when the thread's Start() is called.

Output:

creating the Secondary thread
Secondary thread created
The details of the thread are : 
Thread Name : Seconday_thread
Thread State: Running
Thread priority level:Normal
Secondary thread terminated

For creating threading application, I have mentioned all the methods above, but there some important methods which used regularly for implementing threading.

  1. Thread Join
  2. Thread Sleep
  3. Thread Abort

We have already seen example of Thread.Sleep, now I will show you examples of Thread.Join & Thread.Abort, so you can understand how to implement it.

Thread Join example in C#

Thread.Join() make thread to finish its work or makes other thread to halt until it finishes work. Join method when attached to any thread, it makes that thread to execute first and halts other threads. Now on the same we will see a simple example where we apply Join method to thread.

using System;
using System.Threading;
					
public class Program
{
	public static void Main(string[] args)
        {

            Thread oThread = new Thread(JoinMethod);
            oThread.Start();
            oThread.Join();
            Console.WriteLine("work completed (Main Thread)!");

        }

        static void JoinMethod()
        {
            for (int i = 0; i <= 10; i++)
            {
                Console.WriteLine("work is in progress in JoinMethod !");

            }
        }
}

Output:

work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work is in progress in JoinMethod !
work completed (Main Thread)!

Thread Abort example in C#

The Abort() method is used to terminate the thread. It raises ThreadAbortException if Abort operation is not done.

using System;  
using System.Threading;  

public class MyThread  
{  
    public void Thread1()  
    {  
        for (int i = 0; i < 10; i++)  
        {  
            Console.WriteLine(i);  
            Thread.Sleep(200);  
        }  
    }  
}  
public class ThreadExample
{  
    public static void Main()  
    {  
        Console.WriteLine("Start of Main");  
        MyThread mt = new MyThread();  
        Thread t1 = new Thread(new ThreadStart(mt.Thread1));  
        Thread t2 = new Thread(new ThreadStart(mt.Thread1));  
  
        t1.Start();  
        t2.Start();  
        try  
        {  
            t1.Abort();  
            t2.Abort();  
        }  
        catch (ThreadAbortException tae)  
        {  
            Console.WriteLine(tae.ToString());  
        }  
        Console.WriteLine("End of Main");  
    }  
}  

The output is unpredictable (may throw error also) because the thread may be in running state.