Dot Net Perls

BackgroundWorker Thread Use - C#

by Sam Allen

Problem

Implement threads in your Windows Forms program with BackgroundWorker. Intensive tasks needs to be done on another thread so that the UI doesn't freeze. Implement the code and design as easily as possible. You want to post messages and update the user interface when the task is done.

Solution: C#

On the image, you will see the Visual Studio 2008 designer view. The fly-out panel is the Toolbox, and it contains links for all kinds of controls. It also contains the BackgroundWorker link, which we can use in the following steps.

  1. Click on BackgroundWorker
    You will need to double-click on BackgroundWorker link in the Toolbox (shown in screenshot). Look at the gray bar (tray) near the bottom of your window--a BackgroundWorker will appear there.
  2. Highlight backgroundWorker1
    Click on the backgroundWorker1 item in the gray bar on the bottom. Now, look at Properties panel, which is usually on the right.
  3. ... look for lightning bolt
    You will see a lightning bolt icon in the Properties pane. This is Microsoft's icon for events. BackgroundWorker is event-driven, so this is where we will make the necessary events to use it.
  4. Double-click on DoWork
    Sorry, but we have to get working now. Double-click on DoWork, which will tell Visual Studio to make a new "work" method. In this method, you will put the important, processor-intensive stuff. The next screenshot shows some important parts of the UI.

Those circles show the tray at the bottom where the backgroundWorker1 icon is, and the Properties panel on the right. What the lightning bolt is good for is easily adding events to the BackgroundWorker. This UI is far better than trying to type the methods in manually.

What does DoWork look like?

It is like any other event handler. Here we must look at the C# view of your file, where we will see the DoWork method. You should see that the backgroundWorker1_DoWork event is generated when you double-click on DoWork. For testing, let's add a Thread.Sleep command there.

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(1000); // Very processor-intensive
        }
    }

}

What are some important BackgroundWorker properties?

This is not an exhaustive list, but I want to emphasize the Argument, Result, and the RunWorkerAsync methods. These are properties of BackgroundWorker that you absolutely need to know to accomplish anything. I show the properties as you would reference them in your code.

This object in codeIs used like this in real programs
DoWorkEventArgs eContains e.Argument and e.Result, so it is used to access those properties.
e.ArgumentUsed to get the parameter reference received by RunWorkerAsync.
e.ResultCheck to see what the BackgroundWorker processing did.
backgroundWorker1.RunWorkerAsync(object parameter);Called to start a process on the worker thread.

How can I make it do something?

By adding arguments and return values. Here I show how you must add arguments, invoke the BackgroundWorker, and then receive the results of the thread. You should know enough about threads to know that you can't change variables from multiple threads at once and not have bugs. Here is some example code.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        //
        // Example argument object
        //
        TestObject test = new TestObject
        {
            OneValue = 5,
            TwoValue = 4
        };
        //
        // Send argument to our worker thread
        //
        backgroundWorker1.RunWorkerAsync(test);
    }
}

/// <summary>
/// The test class for our example.
/// </summary>
class TestObject
{
    public int OneValue { get; set; }
    public int TwoValue { get; set; }
}

How do I use DoWork?

Put your expensive code in it. Then, use the DoWorkEventArgs in its body and as its result. Here we hook up the arguments and results from the RunWorkerAsync call. Remember, the TestObject was passed to RunWorkerAsync, and that is received as e.Argument. We also have to cast, as I show in the following code.

/// <summary>
/// Where we do the work in the program (the expensive slow stuff).
/// </summary>
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    //
    // e.Argument always contains whatever was sent to the background worker
    // in RunWorkerAsync. We can simply cast it to its original type.
    //
    TestObject argumentTest = e.Argument as TestObject;

    //
    // Boring....
    //
    Thread.Sleep(10000);

    argumentTest.OneValue = 6;
    argumentTest.TwoValue = 3;

    //
    // Now, return the values we generated in this method.
    // Always use e.Result.
    //
    e.Result = argumentTest;
}

How do I use RunWorkerCompleted?

Go to the lightning bolt in the Designer by clicking on the backgroundWorker1 icon in the tray. Now double click in RunWorkerCompleted. You will get some autogenerated code that looks just like this. Put the argument receiving code in this method.

/// <summary>
/// This is on the main thread, so we can update a TextBox or anything.
/// </summary>
private void backgroundWorker1_RunWorkerCompleted(object sender,
    RunWorkerCompletedEventArgs e)
{
    //
    // Receive the result from DoWork, and display it.
    //
    TestObject test = e.Result as TestObject;
    this.Text = test.OneValue.ToString() + " " + test.TwoValue.ToString();

    //
    // Will display "6 3" in title Text (in this example)
    //
}

Anything else I should know?

You probably know more than you think. BackgroundWorker has a name that might indicate it is more complex (or hard, like 'work') than it really is. There are many more details about threading and abort calls, but once you understand that BackgroundWorker is just a structural "overlay" to threads in Windows Forms, it is quite intuitive. Here are the steps again.

  1. Call RunWorkerAsync with an argument
    You can pass any argument to this method on BackgroundWorker, including null. It simply must inherit from object, which everything does.
  2. Custom processing is run
    Your expensive code is executed in the DoWork method. (Insert pause here as your program does its calculations.)
  3. ... and then it finishes
    When your processing is done, RunWorkerCompleted is called. In this method, you receive the result. In this way, your BackgroundWorker object modifies an object on another thread, and you receive it when it is done.

Summary: using BackgroundWorker

Always use threads when you have a long-running computation in the background. Prefer ThreadPool when you need many threads. Threads aren't as great for I/O operations, as most computers can't multithread disk accesses as well. With the era of multicore systems, we need threads and BackgroundWorker is an excellent shortcut.

Dot Net Perls
About
Sitemap
Source code
RSS
Windows Forms
DataGridView Tips and Secrets
DataTable Example
BackgroundWorker Thread Use
ContextMenuStrip Example
Customized Dialog Box for Windows
Recent
Pi
NGEN Installer Class
List Element Equality
DateTime Tips and Tricks
Remove HTML Tags From String
© 2008 Sam Allen. All rights reserved.