Click here to Skip to main content
Click here to Skip to main content

Multithreaded Programming Using C#

, 10 Oct 2001
Rate this:
Please Sign up or sign in to vote.
A simple tutorial on Multithreaded Programming using C#

Introduction

Threading is a lightweight process. With the help of threads we can increase the response time of the application. To use multithreading we have to use the Threading namespace which is included in System. The System.Threading namespace includes everything we need for multi threading. Now lets see the first program.

Program 1

using System;
using System.Threading;

public class MyThread {

        public static void Thread1() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Thread1 {0}", i);
                }
        }

        public static void Thread2() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Thread2 {0}", i);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                Thread tid1 = new Thread(new ThreadStart(MyThread.Thread1 ) );
                Thread tid2 = new Thread(new ThreadStart(MyThread.Thread2 ) );

                tid1.Start();
                tid2.Start();
        }
}

Let's explore the the whole program.

This program has a class MyThread which has two static functions Thread1 and Thread2. To make a thread you have to make an object of class Thread. The constructor of this class takes a reference of a ThreadStart class. This constructor can send two types of exceptions; ArgumentNullException when the parameter is a null reference or a Security Exception when program does not have permission to create thread.

The parameter of the Thread class is reference to a ThreadStart class. ThreadStart class points to the method that should be executed first when a thread is started. The parameter is the name of the function, which is considered as a thread function. Thread1 is a static function so we give it with the name of class name without making an object of the class. The thread starts execution with Start() method of the Thread class. The output of this program is

Before start thread
Thread1 0
Thread1 1
Thread1 2
Thread1 3
Thread1 4
Thread1 5
Thread1 6
Thread1 7
Thread1 8
Thread1 9
Thread2 0
Thread2 1
Thread2 2
Thread2 3
Thread2 4
Thread2 5
Thread2 6
Thread2 7
Thread2 8
Thread2 9

It is not a requirement that thread function must be static. We can make it a non-static function but in this case we have to create object of that class.

Program 2

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Thread1 {0}", i);
                }
        }

        public void Thread2() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Thread2 {0}", i);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

		MyThread thr = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr.Thread2) );

                tid1.Start();
                tid2.Start();
        }
}

The output of this program is same as previous one.

It is not necessary to make two functions for making two threads. You may have only one thread function and create two threads by creating two objects of the thread class. Take a look at this program.

Program 3

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Hello world {0}", i);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Start();
                tid2.Start();
        }
}

Here we create two objects of class MyThread to execute two threads.

It is also not necessary to create the object of ThreadStart at the time of passing paramaters in the constructor. You can create the object of ThreadStart and pass it as a parameter of Thread. Let's see this program.

Program 4

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Hello world {0}", i);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

		MyThread thr1 = new MyThread();
		MyThread thr2 = new MyThread();

		ThreadStart tStart1 = new ThreadStart(thr1.Thread1);
		ThreadStart tStart2 = new ThreadStart(thr2.Thread1);

                Thread tid1 = new Thread(tStart1);
                Thread tid2 = new Thread(tStart2);

                tid1.Start();
                tid2.Start();
        }
}

This program also gives the same output.

The Sleep method suspends the execution of the thread for a specified time. Sleep is a static method so it can be used without making an object of Thread. There are two overloaded versions of Sleep(), one function takes time in milliseconds and second method take a reference of an instance of the TimeSpan structure.

Program 5

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {
                        Console.WriteLine("Hello world " + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Start();
                tid2.Start();
        }
}

Now the thread sleeps for one millisecond and gives the second thread an opportunity to execute. The output of this program is

Before start thread
Hello world 0
Hello world 0
Hello world 1
Hello world 1
Hello world 2
Hello world 2
Hello world 3
Hello world 3
Hello world 4
Hello world 4
Hello world 5
Hello world 5
Hello world 6
Hello world 6
Hello world 7
Hello world 7
Hello world 8
Hello world 8
Hello world 9
Hello world 9

Now both threads seem to execute in parallel. Here is a program which shows the usage of the other overloaded method of sleep.

Program 6

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        int iHour = 0;
                        int iMin = 0;
                        int iSec = 1;

                        Console.WriteLine("Hello world " + i);
                        Thread.Sleep(new TimeSpan(iHour, iMin, iSec) );
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Start();
                tid2.Start();
        }
}

The TimeSpan structure has four oveloaded constructors. The first take only one long paramater to a specified number of ticks, the second takes three int paramters for hours, mins and seconds respectively, the third take three int parameters for days, hours, mins and seconds and fourth overloaded constructor takes five parameter for days, hours, mins, seconds and milliseconds. The output of this program is the same except this program prints a new value after one second.

The Sleep method throws three type of exceptions; ArgumentException, when the time-out value is less than zero, ThreadInterruptedException when the thread is interrupted while sleeping and SecurityException when caller don’t have the appropriate permissions.

Let's see the program showing to handle the situation when the argument of Sleep() is negative.

Program 7

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        int iHour = 0;
                        int iMin = 0;
                        int iSec = -1;

                        try {
                                Console.WriteLine("Hello world " + i);
                                Thread.Sleep(new TimeSpan(iHour, iMin, iSec) );
                        }
                        catch (ArgumentException ae) {
                                Console.WriteLine(ae.Message );
                        }

                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Start();
                tid2.Start();
        }
}

Message is a property of the ArgumentException class giving the description of the exception. In this program it gives the error message

Parameter Name: Argument must be greater than 0 and less than 2^31 - 1milliseconds.

We can also assign a name to thread by using Name property of Thread.

Program 8

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                tid1.Start();
                tid2.Start();
        }
}

You should catch as many exceptions as possible to avoid run time crashes of the application. Let's try to handle all exceptions, which may be thrown from Sleep.

Program 9

using System;
using System.Threading;
using System.Security;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);

                        try {
                                Thread.Sleep(1);
        				}
                        catch (ArgumentException ae) {
                                Console.WriteLine(ae.ToString() );
                        }
                        catch (ThreadInterruptedException tie) {
                                Console.WriteLine(tie.ToString() );
                        }
                        catch (SecurityException se) {
                                Console.WriteLine(se.ToString() );
        				}
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
        		MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

        		tid1.Name = "Thread 1";
        		tid2.Name = "Thread 2";

                tid1.Start();
                tid2.Start();
        }
}

This program handles all the three exceptions. Take a look at the first line of the code. Here I use one more namespace: System.Security. The exception class SecurityException is defined in this namespace.

We have to get the reference of the thread in our thread function to get the name of the thread. We can get the current thread name by using CurrentThread. CurrentThread is a static property of the Thread class. This property throws only one exception: SecurityException when the caller doesn’t have appropriate Security Permissions.

Now the output of the program is

Before start thread
Thread 1=0
Thread 2=0
Thread 1=1
Thread 2=1
Thread 1=2
Thread 2=2
Thread 1=3
Thread 2=3
Thread 1=4
Thread 2=4
Thread 1=5
Thread 2=5
Thread 1=6
Thread 2=6
Thread 1=7
Thread 2=7
Thread 1=8
Thread 2=8
Thread 1=9
Thread 2=9

There are two methods to terminate thread. The first one is Stop() and second is Abort(). Don’t use Stop(), this function will not be in future releases of .NET; it will give you a warning. There are two overloaded methods of Abort. The first one executed without parameter and the second takes reference of Object as a parameter. This method throws ThreadAbortException that is not caught. Let’s see this program

Program 10

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);

                        try {
                                Thread.Sleep(1);
                        }
                        catch (ArgumentException ae) {
                                Console.WriteLine(ae.ToString() );
                        }
                        catch (ThreadInterruptedException tie) {
                                Console.WriteLine(tie.ToString() );
                        }
                        catch (SecurityException se) {
                                Console.WriteLine(se.ToString() );
                        }
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                tid1.Start();
                tid2.Start();

                try {
                        tid1.Abort();
                        tid2.Abort();
                }
                catch (ThreadAbortException tae) {
                        Console.WriteLine(tae.ToString() );
                }
                Console.WriteLine("End of Main");
        }
}

The output of this program is

Before start Thread
End of Main

This output clearly shows that no thread is executed. Once the thread is aborted then it cannot be started again. Take a look at the following program.

Program 11

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                tid1.Start();
                tid2.Start();

                tid1.Abort();
                tid2.Abort();

                Console.WriteLine("After Abort");

                tid1.Start();
                tid2.Start();

                Console.WriteLine("End of Main");
        }
}

This program throws an exception System.Threading.ThreadStateException. Now try to catch this exception and close the program gracefully rather than terminated abnormally.

Program 12

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                try {
                        tid1.Start();
                        tid2.Start();
                }
                catch (ThreadStateException te) {
                        Console.WriteLine(te.ToString() );
                }

                tid1.Abort();
                tid2.Abort();

                try {
                        tid1.Start();
                        tid2.Start();
                }
                catch (ThreadStateException te) {
                        Console.WriteLine(te.ToString() );
                }

                Console.WriteLine("End of Main");
        }
}

Here we catch ThreadStatException and we use the ToString method of this class. ToString method returns the fully qualified name of this exception, and possibly error message, name of the inner exception and stack trace.

We can also wait for terminating the thread by using the Join method. This method has three overloaded methods. First without parameter waits till the thread dies, second takes one int parameter and waits for the thread to die or for specified time to expire and third take a reference of an instance of the TimeSpan structure. See the following program.

Program 13

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                try {
                        tid1.Start();
                        tid2.Start();
                }
                catch (ThreadStateException te) {
                        Console.WriteLine(te.ToString() );
                }

                tid1.Join();
                tid2.Join(new TimeSpan(0, 0, 1) );

                Console.WriteLine("End of Main");
        }
}

Now the program waits for the first thread until it finishes and second thread for one second.

Threads can be executed in two ways: either in background or in foreground. A background thread is finished when the application is terminated, on the other hand foreground thread is not waiting for the termination of the application. We can set the execution of thread is by using IsBackground property. This program show the usage of this.

Program 14

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                tid1.IsBackground = true;
                tid2.IsBackground = true;

                try {
                        tid1.Start();
                        tid2.Start();
                }
                catch (ThreadStateException te) {
                        Console.WriteLine(te.ToString() );
                }

                Thread.Sleep(10);

                Console.WriteLine("End of Main");
        }
}

The output of this program is

Before start thread
Thread 1=0
Thread 2=0
Thread 1=1
Thread 2=1
End of Main

This output shows when your application terminates that both background threads are also terminated.

We can also assign the priority of our thread. We can assign the priority of a thread by using ThreadPriority priority of the Thread class. This program shows the usage of the priority property of the thread.

Program 15

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {
                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(thr.Name + "=" + i);
                        Thread.Sleep(1);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();
                MyThread thr2 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );
                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );

                tid1.Name = "Thread 1";
                tid2.Name = "Thread 2";

                tid1.Priority = ThreadPriority.Highest;
                tid2.Priority = ThreadPriority.Lowest;

                try {
                        tid1.Start();
                        tid2.Start();
                }
                catch (ThreadStateException te) {
                        Console.WriteLine(te.ToString() );
                }

	        	tid1.Join();
                tid2.Join();

                Console.WriteLine("End of Main");
        }
}

The property of thread 1 is Highest and thread 2 is Lowest. Other possible priority levels are AboveNormal, BelowNormal and Normal.

The GetDomain() method returns the name of executable file in which the thread is executing. This is a static method so we use it with the name of class.

Program 16

using System;
using System.Threading;

public class MyThread {

        public void Thread1() {

		Console.WriteLine(Thread.GetDomain() );

                for (int i = 0; i < 10; i++) {

                        Thread thr = Thread.CurrentThread;
                        Console.WriteLine(i);
                }
        }
}

public class MyClass {

        public static void Main() {
                Console.WriteLine("Before start thread");

                MyThread thr1 = new MyThread();

                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );

                tid1.Start();

        }
}
The output of this program is
Before start thread
Name: prog16.exe
No context policies.

0
1
2
3
4
5
6
7
8
9

This is just an introduction to multithreaded programming using C#.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Zeeshan Amjad
Software Developer (Senior) Bloomberg LP
United States United States
Working as a Sr C++ Developer at Bloomberg LP

Comments and Discussions

 
GeneralThreadings becomes familiar by this introduction Pinmemberskokeh31-May-08 21:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 11 Oct 2001
Article Copyright 2001 by Zeeshan Amjad
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid