Introduction
Everyone knows that free lunch is over. Next generation applications are designed to exploit the power of processors. The challenge is to build software to react to the user's activities as rapidly as possible to provide a rich user experience and at same time, doing the necessary computations to present data to the user as fast as possible. And this is one case where multithreading is useful. There are other cases where multithreading is useful, like:
- Performing operations that take a long time.
- When tasks have varying priorities. Set thread as high-priority to take the time critical tasks and low priority thread performs the other tasks.
- When you want to monitor some resources continuously.
So, there is a lot of benefit for developers to write applications making use of multithreading. As we know, threading is a vast topic and cannot fit in a single article. I am considering a series of articles describing various topics related to multi-threading. As of now, these topics are not yet decided. So just relax, we are about to start the journey into building multi-threading applications.
Creating a New Thread
One can create a thread by using:
Thread thread = new Thread(FunctionName);
First, let's create a console application. Here is the self documented code snippet:
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.Name = "Main Thread";
Console.WriteLine("From Main() function\n");
Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n",
Thread.CurrentThread.Name,Thread.CurrentThread.ManagedThreadId);
Compute compute = new Compute();
Thread Th1 = new Thread(Compute.StaticMethod);
Th1.Name = "Th1";
Th1.Start();
Thread Th2 = new Thread(compute.InstanceMethod);
Th2.Name = "Th2";
Th2.Start();
Console.ReadLine();
}
}
class Compute
{
public Compute()
{
Console.WriteLine("Initialized the Compute class");
Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
}
public static void StaticMethod()
{
Console.WriteLine("From the Static Method in the Compute class");
Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n",
Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.Name);
Thread.Sleep(200);
}
public void InstanceMethod()
{
Console.WriteLine("From the Instance Method in the Compute class");
Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
Thread.Sleep(200);
}
public void DataMethod(object Message)
{
Console.WriteLine("From the Instance Data Method in the Compute class");
string s = Message as string;
if (s != null)
{
Console.WriteLine("Data Received :{0}", s);
Console.WriteLine("Thread ID:{0}\nThread Name:{1}\n",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.Name);
}
}
}
Thread constructor is overloaded, thread class has constructors that take a ThreadStart
delegate or ParameterizedThreadStart
delegate; the delegate wraps the method that is invoked by the new thread when you call the Start
method. Here is the code snippet showing only Threads creation using ThreadStart
delegate. Compute
class remains the same.
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.Name = "Main Thread";
Console.WriteLine("From Main() function\n");
ThreadStart tStartStatic = new ThreadStart(Compute.StaticMethod);
Thread Th1 = new Thread(tStartStatic);
Th1.Name = "Th1";
Th1.Start();
Compute compute = new Compute();
ThreadStart tStartInstance = new ThreadStart(compute.InstanceMethod);
Thread Th2 = new Thread(tStartInstance);
Th2.Start();
Th2.Name = "Th2";
Console.ReadLine();
}
}
Here is the code snippet for creating thread using "ParameterizedThreadStart
".
class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.Name = "Main Thread";
Console.WriteLine("From Main() function\n");
Compute compute = new Compute();
ParameterizedThreadStart pThreadStart =
new ParameterizedThreadStart(compute.DataMethod);
Thread thread = new Thread(pThreadStart);
thread.Name = "Parameterized Thread";
thread.Start("Hello World");
Console.ReadLine();
}
}
Let's have one callback delegate which is triggered when thread completes its work. Here is the code snippet:
namespace ConAppThreadingEx2
{
class Program
{
static void Main()
{
Thread.CurrentThread.Name = "Main Thread";
Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n",
Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);
CallBackDelegate cb = new CallBackDelegate(ResultFromThread);
Compute compute = new Compute(50, cb);
Thread t = new Thread(new ThreadStart(compute.ThreadPro));
t.Name = "ThreadPro";
t.Start();
Console.Read();
}
static void ResultFromThread(int i)
{
Console.WriteLine
("Thread just returned from the ThreadPro method performing:{0} Tasks", i);
}
}
public delegate void CallBackDelegate(int i);
class Compute
{
int numberOfTasks;
CallBackDelegate CallBackDlg;
public Compute(int Tasks, CallBackDelegate cb)
{
numberOfTasks = Tasks;
CallBackDlg = cb;
}
public void ThreadPro()
{
Console.WriteLine("From ThreadPro Method in Compute Class");
Console.WriteLine("Thread Name:{0}\nThread ID:{1}\n",
Thread.CurrentThread.Name,Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
if (CallBackDlg != null)
CallBackDlg(numberOfTasks);
}
}
}
Let’s put all this learning into a bigger example – where we use a GUI application to demonstrate the creation, starting, pausing, resuming and stopping of a thread. Here, a new thread is created to draw the image. This is an example where a color image is converted to grayscale; here the conversion is deliberately made slow since the main purpose is to demonstrate threading – and the user can visually see the pausing, resuming and stopping of threads. The interesting part of the application is thread suspend and thread resume and also communication between thread and UI thread using this.Invoke()
.
Controls like button, text box, label, etc. are created by UI thread. When a new thread is created and during the work flow on a new thread, the new thread will not have any control over these control objects on UI, because these are created by UI thread. There is a solution for such situations where worker thread can call Invoke()
function on a control which in turn triggers the function through a delegate. The next interesting part of the application is suspending, resuming and aborting of a thread. Aborting a thread, when thread is running (check the ThreadState
for its state) is straight forward. But when thread is suspended or request for suspended is the thread state, in such case, thread abort may result in crash.
The CLR will not abort a suspended thread but it will take thread to a state where Garbage Collector can reclaim the memory. So, the best solution would be resume the suspended thread and abort the thread.
Take a look at the ThreadingA
source code.
History