65.9K
CodeProject is changing. Read more.
Home

Threading and UI

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (12 votes)

Dec 13, 2004

2 min read

viewsIcon

74367

downloadIcon

1355

A simple asynchronous threading example for keeping the UI code relatively free from worker thread delegate proxy declarations.

Introduction

Communicating across a network can be time consuming manifesting itself as an unresponsive user interface. Avoiding long and non-deterministic operations on the UI thread will not increase overall performance but it will give the user some degree of confidence that the application hasn’t silently died. The solution seems rather simple; spawn another thread for the lengthy operation. The one gotcha in this scenario is that if you attempt to update the UI from the spawned thread, you will experience all sorts of nastiness in your application. It is very important that you do not break the thread affinity rule when operating upon form resident controls. If you want to manipulate a control from a worker thread, the update must be marshaled to the thread that created the control.

Design Solution

After some experimentation, I noticed the ease with which I was cluttering up the form code with delegate invocations in order to ensure the proper thread affinity when talking to controls. At this point, I decided to encapsulate the long running operations within a separate task object which utilized the Event Handling mechanism to report back upon its completion.

My initial design was mirrored after a Task pattern I found somewhere within MSDN. The only bit I wasn’t very keen on was that the form code still needed to be aware that it might be called upon by a worker thread. That fact necessitated the use of an additional delegate to ensure the thread that created the control was the thread that updated the control.

// Event Handler
protected void LoopDoneHandler(double Calculations, int Count)
{
   this.BeginInvoke(new LDHandler(LDoneHandler),new Object[]{Calculations,Count});
}

// Delegate Declaration & Definition
// Execution must occur on the UI thread
public delegate void LDHandler(double Calculations, int Count);
public void LDoneHandler(double Calculations, int Count)
{
   // update form controls
   btnRunLoops.Enabled = true;
}

My goal was to hide all the implementation details. That meant that I needed the Task to be aware of the object that subscribed to the event. After a bit of digging, I discovered that I could query the event subscriber (class instance) in order to determine if it is a control.

If the class instance is found to be a control, the code calls the BeginInvoke method which executes a delegate on the thread that owns the control’s underlying window handle. All other class instances would have their method executed within the current thread.

   // determine if the object on which this delegate was invoked is a UI thread
   // if the object is a control, then the object is a UI thread
   if (LoopComplete.Target is System.Windows.Forms.Control)
   {
    // make sure execution is done on the UI thread
    System.Windows.Forms.Control t = 
        LoopComplete.Target as System.Windows.Forms.Control;
    t.BeginInvoke(LoopComplete, new Object[]{varTotalAsOfNow, varLoopValue});
   }
   else
    // object from the invocation list isn't a UI thread. 
    LoopComplete(varTotalAsOfNow, varLoopValue);

Summary

IMHO, making these few alterations improves the functionality of the Task pattern. The Form based code can be built without needing to be concerned about thread affinity issues. It is my conjecture that the code is easier to follow and as such easier to maintain.

Thinking out loud; I am wondering if this pattern can be made more generic through templates and or generics? Hmmm; something to consider.