Everyone has probably experienced it already : client applications that freeze after starting a particular operation of an application. Control is only given back to the user interface after the operation has done its work. Very frustrating indeed!
When developing an user-interface for a client applications, you should always prevent that the main thread of your application executes long-running tasks. The main thread is responsible for starting up the application and does control the user interface from start to finish. That's why it's often called the main UI thread and blocking it means serious trouble.
A small example : a Windows Form application that displays the date and time of the day. The time is each second updated with a Timer-conrol. When clicking the Start-button an operation is started that takes 5 seconds. If you code this in a single-threaded application, you will notice that the time won't be updated on the form for 5 seconds. After the operation has finished, the main thread will be able again to update the date and time. Conclusion : not good! Don't slow down the user interface or your users will freak out.
The non UI-operation of 5 seconds should be seen as a background operation and should therefore be placed on a separate thread (multi-threading). In that way, the main UI thread won't be blocked and the time can still be updated on the form during the operation.
The easiest way to run code on a separate thread is to use
asynchronous delegate invocation. I won't dig deeper into what delegates precisely are - but it's ok for now to say that delegates enable you to call/invoke other methods. To invoke a delegate asynchronously (delegates are usually invoked synchronously), you should call the BeginInvoke method. This will queue the method to be run on a thread from the system thread pool. The main thread will return immediately without waiting for the method to finish.
private void doWork()
{
try
{
if (System.Threading.Thread.CurrentThread.Name == null)
{
//Give BackgroundThread a name
System.Threading.Thread.CurrentThread.Name = "WorkerThread";
}
//Sleep for 5 seconds
Console.WriteLine(System.Threading.Thread.CurrentThread.Name + " will sleep for 5 seconds.");
System.Threading.Thread.Sleep(5000);
MessageBox.Show("Work is done !");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
For this example it was maybe better to use the MethodInvoker Delegate because MethodInvoker provides a simple delegate that is used to invoke a method with a void parameter list (see doWork-method) but it works the same way.
MethodInvoker methodInvoker = new MethodInvoker(this.doWork);
methodInvoker.BeginInvoke(null, null);
It's also a good practice to give your threads a name. I gave the main thread the name "Main UI Thread" and the worker thread the name "Worker Thread" (straightforward isn't it) ...
System.Threading.Thread.CurrentThread.Name = "Main UI Thread";
Doing this means that you can check the thread [Threads Window] you're dealing with while debugging.
So now the main UI thread won't be blocked and the actual processing is done on the background/worker thread. That's what we want! I will go a little bit deeper in a next post : passing parameters to a delegate, exception handling, callback methods, AsyncResult, updating GUI-controls from a worker thread, BackgroundWorker in .NET 2.0, ...