Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Thread Synchronization - UI Thread and Worker Thread

4.00/5 (6 votes)
15 May 2010CPOL7 min read 69.7K  
This article describes how a Worker thread can take control of the UI and can update the UI, created by the UI thread.

Introduction

Have you ever faced any problem with your UI based application when you are making a long database operation or calling a long running service or making any network connections like connecting your CD drive, etc.? I think, your answer to this would be 'Yes' whereas you would have wanted your UI application to continue to respond to your actions while the above like operations going in the background, and to display the results on the UI as soon the background operations are completed. I would like to have this, wouldn't you?

Background

The simple answer to this, is to create a multithreaded application and have different threads to manage different operations. People like to use the word “multithreading” when they talk in the context of software programming but it is not a simple task to write multithreaded programs. It can make the entire application down and can take weeks to find and fix the problem.

Any application (to avoid any argument for now, I would say Win UI application) by default runs on one thread as thread is the logical entity which is responsible for executing a program in a process area allocated by the operating system. Since this thread is responsible for creating, drawing and managing the visual controls, so generally this default thread is called UI thread and the thread responsible for executing operations/methods in the background mode is often called a worker thread.

As a multithreading thumb rule, the thread responsible for creating the visual controls must also manage them throughout their lifetime and the Worker thread should never try to manage them.

Till this point, it is not something new that I said, every one of us knows this :-). In this article, I will explain how a worker thread can interact with the visual controls directly.

Worker Thread Taking the Control of UI

Recently, I was working on a requirement where the UI had to process the user entered data by putting them (in the form of a Request object) to a Request Queue and at the same time the UI had to display both the response and error received from the Queues (business logic is on the other side of the queue). But the problem was – I couldn't use the same UI thread for polling 3 different queues (Request Q, Response Q and Error Q) at the same time because one thread would be blocked while waiting for new messages in the Queue and could not be used for other activities. So the only option was to write a multithreaded program with 3 threads – one for each queue. So I chose to have UI thread to receive the user inputs and put that into the Request Q and created two worker threads – one would be constantly polling the Response Q and the other would be polling the Error Q.

  1. UI thread - receives user input and puts that to Request Q
  2. Worker Thread#1 - looks for new messages in the Response Q and displays on the UI (let’s say in a List box)
  3. Worker Thread#2 - looks for new messages in the Error Q and displays on the UI (let’s say in a List box)

There were two approaches to solve the problem:

  1. Let the UI thread poll both the worker threads sequentially and display the data received from response and error queues. But in this case, the UI thread would wait for both the threads to complete indefinitely leaving the UI unresponsive.
  2. When the worker thread finds a new message in the queue, then it will display the data on the UI control directly without relying on the UI thread to carry out this activity.

I know there are many ways to do this in .NET and also there are many articles on thread synchronization and how the worker threads can join the main thread. But if I have a solution to Approach#2, then nothing would be better than that.

The code for sending the user input data to Request Q will be simple, right? The code will look something like this:

C#
public static void Send()
{
	System.Messaging.Message message = new System.Messaging.Message();
	message.Formatter = new BinaryMessageFormatter();
	RequestMessage reqMessage = new RequestMessage();

	try
	{
		//Can set collected data from the UI
		reqMessage.MsgText = "Message# " + (++_Counter).ToString();
		reqMessage.Status = ProcessingStatus.SentFromClient;

		message.Body = reqMessage;
		RequestQ.Send(message, MessageQueueTransactionType.Single);
	}
	catch (Exception ex)
	{
		Logger.HandleError(reqMessage, ex);
	}
}

In my opinion, the simplest way to run code on a worker thread is to use asynchronous delegate invocation. To invoke a delegate asynchronously, call BeginInvoke which will run the method on a thread from the .NET thread pool. In this case, UI thread will return immediately without waiting for the worker thread to complete.

Instead of declaring your own delegate for invoking a method which doesn't expect any input parameters, you can make use of MethodInvoker delegate in System.Windows.Forms namespace. If your method is running on a worker thread, expects any input parameters then you should declare your own delegate.

MethodInvoker delegate:

C#
private void Form_Load(object sender, EventArgs e)
{
	MethodInvoker mi = new MethodInvoker(LongRunningMethod);
	mi.BeginInvoke(null, null);
}

In my case, I needed to pass one parameter. So, I created my own delegate that takes input parameters.

C#
delegate void MethodAsyncCallDelegate(IConsole console);

private void Form_Load(object sender, EventArgs e)
{
	//ReceiveResponse() method of MessageProcessor watches the Response Q
	new  MethodAsyncCallDelegate(MessageProcessor.ReceiveResponse)
				.BeginInvoke((IConsole)this, null, null);

	//ReceiveError() method of MessageProcessor watches the Error Q
	new MethodAsyncCallDelegate(MessageProcessor.ReceiveError)
				.BeginInvoke((IConsole)this, null, null);
}

To understand the code better, let’s look at my ReceiveResponse/ReceiveError code. Your code will be something based on your requirement.

C#
//This runs on a worker thread
internal static void ReceiveResponse(IConsole console)
{
	_Console = console;

	MessageQueue _myQueue = new MessageQueue
		(AppConstants.RESPONSE_QUEUE_PATH, QueueAccessMode.Receive);
	_myQueue.Formatter = new BinaryMessageFormatter();

	//Infinite Loop
	while (true)
	{
		//Waits here till it finds a message in the Queue
		System.Messaging.Message msg = _myQueue.Receive();
		ResponseMessage responseMessage = (ResponseMessage)msg.Body;

		//*** DISPLAY THE RESPONSE MESSAGE ON THE UI
	}
}

//*** DISPLAY THE RESPONSE MESSAGE ON THE UI - Here I need to show the response data received from the queue, on the UI. Now I will tell you an interesting thing (if you already know, please ignore the rest of the article?) that will allow the worker thread to interact with the UI thread.

The Invoke method of the Control class allows any worker thread to make calls. The Invoke method takes a delegate and an optional parameter list and calls the delegate on the UI thread, regardless of which worker thread makes the Invoke call. But please note that this mechanism works only if the UI thread is not currently blocked and the processing starts as soon as the UI thread is ready to process. This doesn't block the UI thread and runs in the background while the user continues to enter the data on the UI. The Invoke method arranges for a thread switch and calls the method wrapped by the delegate on the UI thread. In this case, the worker thread is blocked till the method wrapped by the delegate is completed. To avoid blocking of the worker thread, there is also an asynchronous version of Invoke, which returns immediately and executes the method on the UI thread. Like any other asynchronous delegate method, the Control class also has a corresponding Begin method - BeginInvoke which allows the invocations to run asynchronously on the UI thread.

To take care of my requirement, I have done the following:

  1. Created a method on the UI - DisplayResponse to be called from the above ReceiveResponse mode directly.
  2. Inside DisplayResponse, calling UI Control’s method to display response data.

So, the //*** DISPLAY THE RESPONSE MESSAGE ON THE UI is replaced by:

C#
_Console.DisplayResponse(responseMessage.MsgText, responseMessage.MessageID);

where _Console is the reference to the UI that was passed to ReceiveResponse method as an input parameter. The DisplayResponse method of the _Console is something like this:

C#
delegate void PopulateDelegate(string msgText, string msgID);

void IConsole.DisplayResponse(string msgText, string msgID)
{
	if (InvokeRequired)
	{
		string[] args = new string[] { msgText, msgID };
		this.BeginInvoke(new PopulateDelegate(PopulateResponse), args);
	}
	else
		PopulateResponse(msgText, msgID);
}

private void PopulateResponse(string msgText, string msgID)
{
	lstResponse.Items.Add("Received response for - " + 
			msgText + " and Message ID: " + msgID);
}

In the DisplayResponse method, the Controls handling method (PopulateResponse) is executed on the UI thread by calling the BeginInvoke method of the Form control. Till this point, the code is executed on the worker thread. The InvokeRequired property of Form control is checked to ensure that the calling thread is a worker thread. If not, then the PopulateResponse method is called directly instead of an asynchronous delegate method. This InvokeRequired property check is not required if we are very sure that the DisplayResponse will always be called from a worker thread.

Conclusion

I believe, for you (if you already do not know this) this is a new approach using which you can synchronize your UI and Worker threads in a more efficient way than UI thread waiting for the worker thread to signal or complete.

This particular solution or approach would be extremely useful if the worker thread needs to notify the UI thread about the progress of the background work and the UI thread needs to show the progress of the background work in the form of a Progress Bar without disrupting the user work.

I request all readers of this article to provide their valuable comments.

History

  • 15th May, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)