Basic use of .Net Background Worker Class

INTRODUCTION

This little article just tries to point out some important features (from my view) about how BackgroundWorker class works. More in depth, I would like to show what type of threads are executing each piece of code contained in event handlers being consumed by  BackgroundWorker objects. If you don't know what a thread is, I strongly recommend that you read a little more about them at MSDN or in any other article like here.

BACKGROUND

BackgroundWorkerclass is contained in System.ComponentModel namespace and it is used for executing tasks on other separated and dedicated worker threads different from the main one. So, you can execute in the background time-consuming or heavy operations whereas the user interface (UI) remains completely available to interact with the end user.

For instance, you may think of using this component to deal with heavy downloads, long delays owing to database operations, transactions, etc. while avoiding the awful issue of having to put up with a user interface that seems as though it has stopped responding.

Although the class lacks some features that you can implement by directly using all the available functionality of threads, it is very straighforward to use and will solve the most part of simple cases that you might come across in your daily programming tasks.

BackgroundWorkercan raise four different types of events (more information at BackgroundWorker Class Microsoft Documentation). We'll put the focus on DoWorkProgressChangedand RunWorkerCompletedevents, firstly giving a brief explanation and secondly, by checking with a very basic application which type of thread is executing the events handlers related to them. Here are the events:

  • Disposed

Occurs when the component is disposed.

  • DoWork

Occurs when BackgroundWorkeris started by calling RunWorkerAsyncmethod. Handlers for this event should never try to update the user interface (henceforth the UI) because they will be executed on other secondary threads different from the thread that created the BackgroundWorker object.

  • ProgressChanged

Occurs while BackgroundWorkeris executing some task and ReportProgressmethod is called.

The call to the ReportProgress method is asynchronous and returns immediately. This way, worker threads executing code in the background make a call to ReportProgressmethod and then, the thread that created the BackgroundWorker object is invoked to handle ProgressChanged event.

Here is where you have to place your code to update UI elements bearing in mind that BackgroundWorker object should have been created by the main thread. Then, we'll fulfill the basic requirement that elements in UI must be updated only by the main thread. Otherwise, problems will appear in your application without any doubt.

Finally, the WorkerReportsProgress property value must be true, or ReportProgress will throw an InvalidOperationException.

  • RunWorkerCompleted

Occurs when BackgroundWorkerhas finished its task, has been cancelled or something has gone wrong and an exception has been raised.

USING THE CODE

As it is said in Microsoft Documentation, "to set up for a background operation, add an event handler for the DoWork event. Call your time-consuming operation in this event handler. To start the operation, call RunWorkerAsync. To receive notifications of progress updates, handle the ProgressChanged event. To receive a notification when the operation is completed, handle RunWorkerCompletedevent".

Let's pretend that the initialization of our application is time-consuming and for this reason, we want to show a "Splash Window" with a progress bar before showing the "Main Window". We might write some code similar to the one below:

SplashWindow splashWindow;
MainWindow mainWindow;

private void App_Startup(object sender, StartupEventArgs e)
{
  // Creates and shows the Splash Window.
  splashWindow = new SplashWindow();
  splashWindow.Show();

  // Creates Main Window without showing it. Waits for complete all remaining tasks.
  mainWindow = new MainWindow();

  // Initialize application components using a background worker.
  // Background Worker is created on main thread.
  // It's used to get multi-threading.
  System.ComponentModel.BackgroundWorker worker = new BackgroundWorker();
  worker.WorkerReportsProgress = true;
  worker.DoWork += worker_DoWork;
  worker.ProgressChanged += worker_ProgressChanged;
  worker.RunWorkerCompleted += worker_RunWorkerCompleted;
  worker.RunWorkerAsync();

}

As you can see above, "Splash Window" is showed immediately while "Main Window" is created but not showed. Just after this, a BackgroundWorker object is created to execute a piece of code from worker_DoWork handler, whose progression will be handled by worker_ProgressChanged handler. Once the task is finished, worker_RunWorkerCompletedhandler will be called to close "Splash Window" and show "Main Window".

Here is a picture showing how the "Splash Window" could be like:

Splash Window

Here is the code related to handlers:

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   /* It executes on the thread that created the BackgroundWorker */
   splashWindow.UpdateProgress(e.ProgressPercentage);
}

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
   /* Simulates a heavy task. It is executed on a worker (not main!) thread. */
   for (int i = 0; i <= 10; i++)
   {
     /* The call to the ReportProgress method is asynchronous and returns immediately */
     /* It will trigger ProgressChanged event */
     (sender as BackgroundWorker).ReportProgress(i * 10, i);
     Thread.Sleep(500); 
   }
}
 
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   // Closes splash window
   splashWindow.Close();

   // Shows main window
   mainWindow.Show();
}

The code above is very straightforward and easy to understand as I explained before.

  • A progress bar from the User Interface in "Splash Window" will be updated for showing initialization progression by means of code executed in worker_ProgressChanged handler.

Here is a screen shot with "Threads Debug Window" in Visual Studio:

Thread executing worker_ProgressChanged handler

You can see that it's executed on the Main Thread (Id=6160, the same that created the BackgroundWorker). It's highlighted in the Debug Thread Window and indicated with a yellow arrow.

  • A time-consuming task will be simulated and executed in worker_DoWork handler, calling ReportProgress method to cause an invocation of worker_ProgressChanged handler.

Here is a screen shot with "Threads Debug Window" in Visual Studio:

Thread executing worker_DoWork handler

You can see that it's executed on a secondary dedicated new thread with Id=4732

  • Finally, worker_RunWorkerCompletedhandler will be called when worker_DoWork handler finishes their work.

KEY POINTS AND CONCLUSION

At least, remember these important two key points:

  • Time-consuming operations should be placed in DoWork event handlers. Code will be executed on dedicated and separated new worker threads that never should try update the user interface.
  • The call to the ReportProgress method is asynchronous and returns immediately. The ProgressChanged event handler executes on the thread that created the BackgroundWorker. Here is where you have to put your code to update the UI.
  • The class lacks some advances features but it might be enough for solving the most part of basic requirements you will come across.

 

 

Add comment