Click here to Skip to main content
13,549,045 members
Click here to Skip to main content
Add your own
alternative version


72 bookmarked
Posted 22 Jan 2007

Improved Threading & UI integration under .NET 2.0

, 22 Jan 2007
Rate this:
Please Sign up or sign in to vote.
Improving threading and UI integration with .NET 2.0


The need for background threading is most noticeable in the design of User Interfaces. This is certainly not a new phenomenon. Unresponsive interface issues have been plaguing users and developers alike since the inception of the GUI; at least in the Microsoft Windows arena.

There are no silver bullets which resolve the problem but framework improvements have certainly made it much easier for developers to deal with these types of issues. The tools available for dealing with non-deterministic operations have been improving with each release of the .NET framework. Release 1.1 provided some robust and diverse threading capabilities but it still didn't integrate smoothly with UI code due to thread affinity issues.

In summary, well matured and entrenched STA (Single Threaded Apartment) design constraints permeate throughout Window based controls. This means that only the thread that created the control may manipulate the control. Deviation from this constraint can and will lead to very strange behaving applications. If they behave at all!

The IDE (Integrated Development Environments) which has made the creation of User Interfaces far easier has also made it very easy for code to be developed such that it all runs on the main thread. In and of itself this isn't a problem until the UI activates a long running or non-deterministic operations such as getting data from a database or invoking a web service. All of a sudden, the user experiences slow responses from the application or worse, the dreaded, not responding message in the title bar.

With the tools currently available it is this authors opinion that only control related operations which can respond within 15 to 20 ms (guaranteed) should be executed on the main thread. All other operations should be done on alternate threads. This seemingly Draconian measure looks to be evolving more and more into standard practice. This design constraint is bolstered significantly by the framework improvements that have been coming to market such as true asynchronous ADO operations and the introduction of the Background Worker thread.

Background Worker Thread

The Background worker thread introduced in .NET 2.0 is made available on the Tool palette for Form design. This new tool component has been designed such that it aligns seamlessly with the event handling paradigm used by all the other controls.

The Background worker control is a very interesting when you consider that it has encapsulated all thread affinity issues. It makes it almost too easy to forget you are working across threads.

BackgroundWorker Object Summary

This object can be found within the toolbox for easy dropping on a form. Removes threading concerns while allowing for easy UI updating. The properties of a background worker include:

  • WorkerSupportsCancellation
  • WorkerReportsProgress
  • CancellationPending


The RunAsynch method starts the processing of the code. The work is performed on the worker thread within the DoWork event handler. The worker should check the CancellationPending attribute frequently.

The ProgressChanged and RunWorkerComplete methods both run on the UI thread. (See diagram)

UI Thread Worker Thread

The event handling methods added to the derived Form class look like any other methods. The intriguing and somewhat disconcerting aspect is that there isn't any indication that you are dealing with different threads. I am not saying this is a bad thing. The code is clean and succinct. It is just important to keep in the back of your mind that dead-lock, synchronization and race condition issues are factors you will need to consider when interacting between the main thread and background thread.

Basic Code Structure

// this code is executed on the worker thread, called 
// from UI by RunWorkerAsync</p>

void worker_DoWork (Object sender, DoWorkEventArgs e)
  while (!worker.CancellationPending)
    // do work
    worker.ReportProgress (percentProgress);

// this method is called on the UI thread, invoked by 
// ReportProgress

void worker_ProgressChanged(Object sender, 
                            ProgressChangedEventArgs e)
  progressTextBox.Text = e.ProgressPercentage + "%";

// this code is called on the UI thread once the worker 
// thread terminates (completes)

void worker_RunCompleted(Object sender, 
                         RunWorkerCompletedEventArgs e)
  progressTextBox.Text = "Complete";

During debugging you can clearly see your background thread in action.

Main Thread

Worker Thread

Some Real World Code

I mentioned earlier that ADO has provided true asynchronous capabilities. In prior incarnations of ADO, asynchronous operations were available but at the cost of spawning and blocking on a synchronous thread. ADO 2.0 doesn't work that way. It is truly asynchronous.

I decided to blend the ADO feature together with the background thread and the binding ability of Generics to controls. The results were outstanding. I decided to select a long running operation that would significantly disrupt the UI if executed on the main thread. The results yield a very responsive GUI. Please bear in mind the operation isn't any faster but the perception of usability greatly enhances the application. Depending upon the application, it might actually let the user do something else while she waits for the data.

The DoWork event handler shows the invocation of a length database stored procedure call. The points to take away are that the code checks for a user initiated cancellation and post progress messages frequently.

private void backgroundWorker1_DoWork(object sender, 
                                      DoWorkEventArgs e)
  Object state;

  BackgroundWorker bw = sender asBackgroundWorker;

  int percentProgress = 0;

  // atomic operation
  Interlocked.Exchange(ref percentProgress, tickCounter); 

  string cnString = "Integrated Security=SSPI;
                    Persist Security Info=True;
                    Initial Catalog=pdm_snapshots;
                    Asynchronous Processing=true";
  state = (string)"Running query...";

  using (SqlConnection cn = newSqlConnection(cnString))
      string proc = "spGetGroup";
      SqlCommand cmd = newSqlCommand(proc, cn);
      IAsyncResult result = cmd.BeginExecuteReader();
      while ((!result.IsCompleted) &&
        percentProgress = tickCounter;

        bw.ReportProgress(percentProgress, state);
        Thread.Sleep(5);   // do not monopolize resource. 
                           // relinquish some time
      if (bw.CancellationPending)
        // exit when you reach max running time
        e.Cancel = true;
      // At this point we have a result
      // atomic operation

      percentProgress = Interlocked.Exchange(
                                ref tickCounter, 0); 
      state = (string)"Reading...";
      using (SqlDataReader rdr = cmd.EndExecuteReader(
        while (rdr.Read() &&
          // load collection
          percentProgress = tickCounter;
          bw.ReportProgress(percentProgress, state);

          Thread.Sleep(5);   // do not monopolize resource.
                             // relinquish some time

      if (bw.CancellationPending)
          e.Cancel = true;
      if (!rdr.IsClosed)
  catch (InvalidOperationException ex)
    throw ex;
  catch (SqlException ex)
    throw new InvalidOperationException(ex.Message);

Upon conclusion of the worker thread, the Run worker completed event is called on the main thread. The interesting bits to take away are that thrown events from the background worker are caught and may be tested through the RunWorkerCompletedEventArgs object.

The code also depicts a Generic (List<int>) bind to a control. The Generic serves as the shared memory construct operated upon by both the worker thread and the main Ui thread.

private void backgroundWorker1_RunWorkerCompleted(object
                    sender, RunWorkerCompletedEventArgs e)
  // Run on UI thread
  // raised by the worker on cancellation, completion.

  if (e.Error != null)
    lblPercent.Text = "Error encountered :
                      " + e.Error.Message;
    if (e.Cancelled)
      lblPercent.Text = "Operation cancelled!";

  timer1.Enabled = false;

  Interlocked.Exchange(ref tickCounter, 0);

  progressBar1.Value = 0;
  lstData.UseWaitCursor = false;

  btnGetData.Enabled = true;

  // make final progress update 

  BindingSource bs = newBindingSource();
  bs.DataSource = data;
  lstData.DataSource = bs;


As we accelerate into the realm of multi-cores and exceedingly more complex and loosely coupled systems, the need for easy to use threading functions is ever increasing. The .NET framework is evolving to meet those and other challenges. The background worker thread is but one small example of a productivity enhancement which can be leveraged to provide the best user experiences for our customers.


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
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralA twist on threading Pin
Mikester27-Jan-09 5:39
memberMikester27-Jan-09 5:39 
QuestionAnother question Pin
John Colaizzi18-Nov-08 9:43
memberJohn Colaizzi18-Nov-08 9:43 
QuestionQuestion Pin
Prateek Bahl16-Jul-08 9:51
memberPrateek Bahl16-Jul-08 9:51 
AnswerRe: Question [modified] Pin
Gary J. Kuehn17-Jul-08 9:38
memberGary J. Kuehn17-Jul-08 9:38 
QuestionJust a question [modified] Pin
tq_Tere3-Jun-08 23:15
membertq_Tere3-Jun-08 23:15 
GeneralGreat! Pin
blackjack215010-Dec-07 0:48
memberblackjack215010-Dec-07 0:48 
GeneralPolling and cancellation of worker thread Pin
baruchl29-Jan-07 21:30
memberbaruchl29-Jan-07 21:30 
GeneralRe: Polling and cancellation of worker thread Pin
Gary J. Kuehn30-Jan-07 6:45
memberGary J. Kuehn30-Jan-07 6:45 
GeneralRe: Polling and cancellation of worker thread Pin
Nathan Holt at CCEI18-May-07 11:46
memberNathan Holt at CCEI18-May-07 11:46 
Generalnice but Pin
Sacha Barber24-Jan-07 6:00
memberSacha Barber24-Jan-07 6:00 
GeneralRe: nice but Pin
Gary J. Kuehn24-Jan-07 12:42
memberGary J. Kuehn24-Jan-07 12:42 
GeneralRe: nice but Pin
Sacha Barber1-Feb-07 22:37
memberSacha Barber1-Feb-07 22:37 
GeneralThanks ! Pin
Xs_222-Jan-07 21:56
memberXs_222-Jan-07 21:56 
GeneralVery Informative Pin
Paul Conrad22-Jan-07 19:09
editorPaul Conrad22-Jan-07 19:09 

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

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

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.180515.1 | Last Updated 23 Jan 2007
Article Copyright 2007 by Gary J. Kuehn
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid