Wednesday, October 21, 2009

Threading in .net - for Beginners


Method 1: Using ThreadPool.QueueUserWorkItem

There may be scenarios, in which you want to have an asynchronous operation to be performed without the need of a handle on the function being called. For example, this can be an audit log or a process log operation which can be a background process while your main application performs the important operations. There is a simple single line code to achieve this using ThreadPool.QueueUserWorkItem call. Typical usage is as follows.


private void button1_Click(object sender, EventArgs e)
{
string argument = "Process started";
ThreadPool.QueueUserWorkItem(new WaitCallback(WriteLog), input);
}

private void WriteLog(object o)
{
string argument = o as string;
// Your code to log the argument
}

In this method:
1. With a single line code, you are getting the basic benefits of threading without having to worry about creating, managing and terminating a threaded operation.
2. QueueUserWorkItem call is in control of .net runtime. You will not be able to control the time at which it gets executed.
3. You cannot control the state and priority of the function being executed.

Method 2. Using ThreadStart, ParameterizedThreadStart and Thread

Below is another method that will give more control on your asynchronous operation.

a) Without having a Parameter

Class level decleration


private Thread t = null;


This can be used for later tracking of the thread being executed.

private void button2_Click(object sender, EventArgs e)
{
ThreadStart ts = new ThreadStart(WriteStartLog);
t = new Thread(ts);
t.IsBackground = true;
t.Priority = ThreadPriority.Normal;
t.Start();
}

private void WriteStartLog()
{
// Your code to log the process start
}


To track the thread, the thread object can be used. A sample usage is provided below:

private void button3_Click(object sender, EventArgs e)
{
if (t != null)
{
if (t.IsAlive)
t.Abort();
}
}


b) Passing parameters using ParameterizedThreadStart

When parameters are to be passed to the invoked method, this can be done using ParameterizedThreadStart using the medium as objects.



private void button2_Click(object sender, EventArgs e)
{
ParameterizedThreadStart pts = new ParameterizedThreadStart(WriteLog);
t = new Thread(pts);
t.IsBackground = true;
t.Priority = ThreadPriority.Normal;
t.Start("Process started");
}


private void WriteLog(object o)
{
string argument = o as string;
// Your code to log the argument
}

Now that we have covered the basic skeleton of our article, let’s see the other important aspects of threading.

Handling ThreadAbortException

There might be a need to abort the thread being executed at any instant of time. This can happen due to the user cancellation of the process or any other action like the primary thread being closed due to a various reason. At these scenarios (even when there is no need), it is mandatory to have a ThreadAbortException to be handled in the method which is being called. This can be as follows:



private void WriteLog()
{
try
{
// Your code to log the argument
}
catch (ThreadAbortException tae)
{
//handles the thread abort exception
//code to cleanup - let know the user that thread got cancelled, if necessary.
}
catch (Exception e)
{
//handle other exceptions
}
}


Communicating between main and background thread.

Consider, this scenario:
You are having a background thread to create thumbnails for pictures in a folder. For each thumbnail being created, you have to draw it on the form. That is, to communicate from the background thread to the parent thread with the output from the background thread. This is pretty simple using delegates. Here it is how it’s done.

Class level declarations:


private delegate void addPictureBox2Main_Delegate(PictureBox pbb);
private Thread t = null;



private void button2_Click(object sender, EventArgs e)
{
ThreadStart ts = new ThreadStart(LoadImageThumbNails);
t = new Thread(ts);
t.IsBackground = true;
t.Priority = ThreadPriority.Normal;
t.Start();
}

private void LoadImageThumbNails()
{
try
{
//for each of the thumbnails being created
//generate picturebox with thumbnail here
PictureBox pb = //function to load thumbnail
this.Invoke(new addPictureBox2Main_Delegate(addPictureBox2Main), new Object[] { pb });
}
catch (ThreadAbortException tae)
{
//handles the thread abort exception
//code to cleanup - let know the user that thread got cancelled, if necessary.
}
catch (Exception e)
{
//hanlde other exceptions
}
}


private void addPictureBox2Main(PictureBox pb)
{
//add the picture box pb to the parent control as needed
}


** Code samples are in C#**

Note: This article only covers the basic aspect of threading and it aims to help beginners understand the threading concepts. There is more to it. So, keep learning.

2 comments: