If you develop client applications, very often you come across an operation which is taking long to complete. Basically, you have 2 options; You can either do nothing about it, let the application "freeze" and let user wait for end of the operation. The problem is that the user doesn't know what is happening so he will become frustrated ("Damn, this app is really slow...") and may even eventually kill the application from the task manager, thanks to the "feature" of Windows which will mark the window as "Not Responding" and will offer the user an option to end the process.

That is why asynchronous processing (aka. processing in background) comes in place. The idea is that you display a dialogue to the user that informs him/her that there is something going on and that it may take some time to complete; or you can even let the user work with the application and literally do all the processing in the background.

Follow up:

Process, its threads and a message loop

Before we start, it is important to understand the architecture of multitasking in modern OS, such as Microsoft Windows. Each executable application is running in virtual container called process, which has its own memory space assigned. Inside each process, there is at least one thread, which performs the actual execution of the code. When created a thread is given a procedure (called entry point) which contains the code to be executed by the thread; when the procedure exists, the thread is destroyed too. The thing to remember is that inside one process there can me multiple threads (which run independently), but they share the memory space, so one thread can access to variables of another thread and all threads share same static variables. This means that access to a single variable must be synchronised, but that is a topic for an article itself. For more information about threads and process, you can visit good old Wiki or MSDN Library.

In windows forms application, there is at least one thread (called the UI thread) which is called from the Main method of the application, as shows the following code snippet:

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm()); // Enter the message loop
}

The thread will at first perform some initialisation and then it will enter Application.Run() method, which is the default .NET implementation of message loop. As you probably know, all UI processing in Microsoft Windows is message based. So if a window needs to be drawn, if key is pressed or if you hover over a window with a mouse cursor, the OS will notify the window by sending a corresponding message (e.g. WM_KEYDOWN if a key was pressed). These messages are put into a message queue and they are processed one by one by the message loop. The default .NET window message loop is hidden in Application.LocalModalMessageLoop() and it looks something like this (if you worked with Windows API directly, this must look really familiar to you...):

private bool LocalModalMessageLoop(Form form)
{
try
{
  NativeMethods.MSG msg = new NativeMethods.MSG();
  bool loopRunning = true;
  while (loopRunning)
  {
   Message message;
   if (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 0))
   {
    //There is a message in the queue; obtain it
    UnsafeNativeMethods.GetMessageW(ref msg, NativeMethods.NullHandleRef, 0, 0)

    //Translate message info (e.g. key codes to virtual keys)
    UnsafeNativeMethods.TranslateMessage(ref msg); 

    //Send the message to corresponding window
    UnsafeNativeMethods.DispatchMessageW(ref msg);
   }
   loopRunning = !form.CheckCloseDialog(); 

  } 
  return loopRunning;
}
catch
{  
  return false;
}
}

The PeekMessage is constantly checking if there is a new message in the queue to process. If so, the message is obtained by the GetMessage method and sent to target window by DispatchMessage. The target window does its processing and once the processing is done, the loop continues with another message in the queue. The most important thing to notice is that all the processing is done in one thread, so if processing of a message (e.g. a button click) takes a long time, the processing of all other messages is stopped, so the window will not redraw neither it will process any user input until the operation is finished (which means the user cannot for example cancel the operation by pressing a Cancel button).

Threads in .NET

Since the first version .NET Framework contains a managed class wrapper to create additional threads called System.Threading.Thread. To create a new thread, you need to create new instance of Thread class, supply it with an entry point (through ThreadProc delegate) and call Start() method to begin the execution. One should not expect that the execution will start immediately after you call Start() method, this really depends on the OS scheduler and it may vary amongst various PCs.

Following code shows creation of additional thread on button click to avoid freezing the form while the operation is executed:

using System.Threading;

private void btnStart_Click(object sender, EventArgs e)
{
Thread task = new Thread(TaskProcedure);
task.Start();
}

private void TaskProcedure()
{
for (int i = 0; i < 1000000; i++)
  CallSomeProc();
}

I generally do not recommend using individual Thread instances to perform background processing, because it creates additional overhead and it is less efficient than ThreadPool (see below). Thread instance should be used for threads which run for most of the application's life time, e.g. if my application needs to monitor connectivity while it is running, it is probably a good idea to start a separate Thread instance on the start-up and let the thread notify the app if the connectivity changes.

Thread pool

Since creation of a new thread is quite expensive operation and the start of the tread is not deterministic (because the Start() method marks a Thread as runnable, but it does not start the execution immediately), using Thread class for background processing is not the best thing to do.

That is where the thread pool pattern (and its ThreadPool implementation in .NET) comes into action. The idea behind a thread pool is quite simple - when the thread pool is initialised, it creates defined number of threads (which can be controlled by SetMinThreads and SetMaxThreads methods)  and sets them into sleep mode (in order not to consume CPU time) - these threads are waiting for a task to be assigned to them.

To start background processing of new operation you just need to call QueueUserWorkItem() method and supply it again with an entry point. The thread pool takes a first thread which is in sleep mode and use the thread to execute the entry point method. Unlike using Thread class, the ThreadPool class has its threads already running so the execution of the task can start immediately and there is no overhead to create the thread, which makes ThreadPool a good candidate for background processing. Once the processing of the entry procedure is finished, the thread returns to the pool of "free" threads ready for next task.

ThreadPool should be used for processing which may take some time, but it should not be running indefinitely until the application exists, since this would take the thread from the pool but never return it.

Following code shows the same situation like previous section - we perform a long operation and we want to avoid freezing of the UI:

using System.Threading;

private void btnStart_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(TaskProcedure, null);
}

private void TaskProcedure(object myParameter)
{
for (int i = 0; i < 1000000; i++)
  CallSomeProc();
}

Cross-thread operations

Most of the time we want the background thread to display some progress status on the UI (e.g. percent of downloaded file, etc.). The problem is, that operations with Windows Forms and their controls are not thread-safe, which means if you try to set a property or call a method from other than the UI thread which created the window the result of such operation is undefined - you may end up in inconsistent state, racing condition or it can even lead to a dead-lock situation.

Let's say I want to inform the user that the operation has finished once the task finishes. The first approach would look like this:

private void btnStart_Click(object sender, EventArgs e)
{
this.lblProgress.Text = "Starting processing";
ThreadPool.QueueUserWorkItem(TaskProcedure, null);
this.lblProgress.Text = "Processing started";
}

private void TaskProcedure(object myParameter)
{
for (int i = 0; i < 1000000; i++)
  CallSomeProc(); 

this.lblProgress.Text = "Done";
}

The problem with the code above is called racing condition - it is not sure if the text will be set to "Done" or "Processing started", because this depends on which command will be called the last and since both code sequences are running in parallel you cannot explicitly say what the outcome would be - maybe the btnStart_Click event handler will finish first, maybe it will be the TaskProcedure method.

These time dependent errors are usually very hard to find, fortunately Visual Studio debugger automatically checks if the calls to the control are made exclusively from the UI thread (this validation is not however present in running environments). If you try execute the code above, you will get an InvalidOperationException saying "Control lblProgress accessed from a thread other than the thread it was created on.".

So what is the solution? We need to execute all operations directly in the UI thread, so we need a method of telling the UI thread to execute our piece of code. This method is called BeginInvoke. The method is thread-safe (it can be called from any thread without problems) and it execute specified delegate using the UI thread. Basically, if you call this method, .NET will add new message to the message queue with "pointer" to the delegate and once the message loop reaches this message, it will call the supplied delegate. As a result, you cannot be sure when the delegate will be called (it depends when it will be picked up by the message loop), but you can be sure it will run from the UI thread so you can perform any operations with the UI objects.

So let's fix the code above by using the BeginInvoke method:

private void btnStart_Click(object sender, EventArgs e)
{
this.lblProgress.Text = "Starting processing";
ThreadPool.QueueUserWorkItem(TaskProcedure, null);
this.lblProgress.Text = "Processing started";
}

private void TaskProcedure(object myParameter)
{
for (int i = 0; i < 1000000; i++)
  CallSomeProc();  

this.BeginInvoke(new MethodInvoker(SetDoneProgress), null);
}

private void SetDoneProgress()
{
this.lblProgress.Text = "Done"; 
}

Now the code should be working correctly, because we can be sure that the app will first complete the btnStart_Click message processing and then it will move to following messages and one of them will be the one to call SetDoneProgress.

LukeN 080610

Technorati:

Contact Us


Are you interested in professional services of the developers who publish on this blog? Contact us on our web site now.

XML Feeds

Add to Technorati Favorites
February 2012
Mon Tue Wed Thu Fri Sat Sun
 << <   > >>
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29