Click here to Skip to main content
Click here to Skip to main content

Threading and UI

, 12 Dec 2004
Rate this:
Please Sign up or sign in to vote.
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Gary J. Kuehn
Engineer
United States United States
No Biography provided

Comments and Discussions

 
QuestionBetter method? Pinmemberdfhgesart16-Aug-07 4:36 
AnswerOr Pinmemberdfhgesart18-Aug-07 11:11 
GeneralRe: Or PinmemberGary J. Kuehn18-Aug-07 12:05 
GeneralGood article! PinmemberDude 45675-Jul-07 5:58 
QuestionGreat intro. How would this be added? PinmemberDarchangel9-Sep-05 6:07 
AnswerRe: Great intro. How would this be added? PinmemberGjk10-Sep-05 4:09 
GeneralExcellent Pinmembermike griggs7-Apr-05 21:35 
GeneralThanks! PinmemberRobTheBob8-Mar-05 4:54 
Thanks alot this tutor was just what i needed. Thanks to you i can now continue my project. Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 13 Dec 2004
Article Copyright 2004 by Gary J. Kuehn
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid