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#.