Big Ball of Mud

Thread Marshalling Part 2 – using BackgroundWorker

leave a comment »

The pattern described in the prevoius post is so common, that .NET 2.0 introduced a new tool to make it easier: BackgroundWorker. Let’s rewrite the code of our form to take advantage of this component:

    
    1     public partial class Main : Form
    2     {
    3         private readonly EmptyLoop _emptyLoop = new EmptyLoop(1000000000);
    4         private readonly BackgroundWorker _bgWorker = new BackgroundWorker();
    5
    6         public Main()
    7         {
    8             InitializeComponent();
    9         }
   10
   11         private void bStart_Click(object sender, EventArgs e)
   12         {
   13             progressBar.Visible = true;
   14             bCancel.Enabled = true;
   15
   16             _emptyLoop.UpdateProgress += algorithm_UpdateProgress;
   17
   18             _bgWorker.WorkerReportsProgress = true;
   19             _bgWorker.WorkerSupportsCancellation = true;
   20             _bgWorker.DoWork += (worker, eventArgs) => _emptyLoop.Go();
   21             _bgWorker.ProgressChanged += (worker, eventArgs) =>
   22                 ShowProgress(eventArgs.ProgressPercentage);
   23             _bgWorker.RunWorkerCompleted += (worker, eventArgs) =>
   24                 ShowAlgorithmCompleted();
   25             _bgWorker.RunWorkerAsync();
   26         }
   27
   28         private void ShowProgress(int percent)
   29         {
   30             progressBar.Value = percent;
   31         }
   32
   33         private void ShowAlgorithmCompleted()
   34         {
   35             progressBar.Value = 0;
   36             progressBar.Visible = false;
   37             bCancel.Enabled = false;
   38         }
   39
   40         private void algorithm_UpdateProgress(object sender,
   41             EmptyLoop.UpdateProgressEventArgs e)
   42         {
   43             if (!_bgWorker.CancellationPending)
   44             {
   45                 _bgWorker.ReportProgress(e.Percent);
   46             }
   47             else
   48             {
   49                 _emptyLoop.Cancel();
   50             }
   51         }
   52
   53         private void bCancel_Click(object sender, EventArgs e)
   54         {
   55             _bgWorker.CancelAsync();
   56         }
   57     }

Looks familiar but there is number of significant differences from prevoius implementation:

  1. The functionality provided by BackgroundWorker class is quite similar to the design of EmptyLoop class. This is not by accident, operations like: reporting progress, cancelling and completing are in fact common to asynchronous operations. We still need to send a message about the progress report from our EmptyLoop and as we would like to keep its implementation clean from any unnecessary dependencies, events are the best way.
  2. Canceling operation is quite different. Cancel button calls BackgroundWorker CancelAsync method, which sets CancellationPending flag to true. Normally DoWork event handler should check this flag and cancel working. In our case we do not have a way to pass the canceling message from DoWork handler, instead we check the cancelling flag in report progress method. This will stop the algorithm a little bit later, but still works.
  3. Finally: thread marshalling method calls: Invoke or BeginInvoke are gone! This is because BackgroundWorker marshals event calls automatically.

The last information is quite interesting, especially if you take into account that the BackgroundWorker lives in System.ComponentModel namespace and is supposed to be a generic asynchronous component – it can be used in WinForms, WPF and any environment you like. Somehow it knows how to do things like thread marshalling.

If we take a look into its RunWorkerAsync method, we can see that it takes advantage of the AsyncOperationManager static class. This static class has one property SynchronizationContext and one method CreateOperation. This members allow the hosting environment (like Windows Forms) set its SynchronizationContext – the object which describes the behaviour required for asynchronous operations. What is interesting, as this an extensible model, we can try to use this mechanism for our purposes.

Advertisements

Written by bigballofmud

2009/03/28 at 9:46 pm

Posted in C#, Multithreading

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: