BackgroundWorker Class (Multi-Threading)


While learning C Sharp I quickly became aware of the fact that I could not interact with the user interface after I kicked off a section of code (ie. press a playbutton). After some research I found out that the Windows forms UI applications runs on a single cpu thread. This basicly means that if the program is busy the user is locked out of the user interface and can no longer interact with it.

Enter the BackgroundWorker Class! Microsoft's official description over at MSDN:

The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.

(Image) The event names in the image above reveal the flow of the BackgroundWorker class.

  • The bgw_someName.RunWorkerAsync(); kicks of the BackgroundWorker.
  • The code you put in the DoWork function is the work you want to offload to the background thread.
  • The ProgressChanged function can be called upon from within the DoWork function by using ReportProgress(). This is very usefull for updating things on the UI, for example progress bars.
  • The RunWorkerCompleted is executed on the UI thread after the DoWork function is completed and has reported its result.

Translating the explanation above into the actual code.

In Visual Studio, while in the design area of your form, search your Toolbox for the BackgroundWorker component and drag it onto your form. It will appear in the form's component tray.

Define your background worker:

bgw_someName = new BackgroundWorker();

Hook up the events and create a background worker thread that ReportsProgress & SupportsCancellation:

bgw_someName.DoWork += Bgw_someName_DoWork;
bgw_someName.ProgressChanged += Bgw_someName_ProgressChanged;
bgw_someName.RunWorkerCompleted += Bgw_someName_RunWorkerCompleted;
bgw_someName.WorkerReportsProgress = true;
bgw_someName.WorkerSupportsCancellation = true;

Setup the RunWorkerCompleted function:

// On completed background worker do the appropriate task
void Bgw_someName_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
     // The background process is complete. We need to inspect
     // our response to see if an error occurred, a cancel was
     // requested or if we completed successfully. 
     if (e.Cancelled)
     {
         lblStatus.Text = "Playback cancelled.";
     }


     // Check to see if an error occurred in the background process.
     else if (e.Error != null)
     {
         lblStatus.Text = "Error during playback operation.";
     }
     else
     {
         // Everything completed normally.
         lblStatus.Text = "Playback complete.";
     }

     //Change the status of the buttons on the UI accordingly
     btnStartAsyncOperation.Enabled = true;
     btnCancel.Enabled = false;
}

Setup the ProgressChanged function:

// The progress bar is updated here on every called ReportProgress(); 
void Bgw_someName_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
     // Update the progressBar with the integer supplied to us from the
     // ReportProgress() function.  
     pbr_PlayProgression.Value = e.ProgressPercentage;
     lblStatus.Text = "Playing ... " + pbr_PlayProgression.Value.ToString() + "%";
}

Setup the DoWorkfunction. This is the place where the actual busy work is offloaded to the background thread:

// The progress bar is updated here on every called ReportProgress(); 
void Bgw_someName_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
     // Update the progressBar with the integer supplied to us from the
     // ReportProgress() function.  
     pbr_PlayProgression.Value = e.ProgressPercentage;
     lblStatus.Text = "Playing ... " + pbr_PlayProgression.Value.ToString() + "%";
}

Call upon the BackgroundWorker functions above.

Calling on the functions that where defined in the code above is quite simple. Create new buttons via the Toolbox and enter the following in the Click events.

Kicking off the BackgroundWorker:

private void btnStartAsyncOperation_Click(object sender, EventArgs e)
{
     //Change the status of the buttons on the UI accordingly
     //The start button is disabled as soon as the background operation is started
     //The Cancel button is enabled so that the user can stop the operation 
     //at any point of time during the execution
     btnStartAsyncOperation.Enabled = false;
     btnCancel.Enabled = true;

    // Kickoff the worker thread to begin it's DoWork function.
    bgw_someName.RunWorkerAsync();
 }

Cancelling the BackgroundWorker:

 private void btnCancel_Click(object sender, EventArgs e)
{
      if (bgw_someName.IsBusy)
      {
      // Notify the worker thread that a cancel has been requested.
      // The cancel will not actually happen until the thread in the
      // DoWork checks the bgw_someName.CancellationPending flag. 
      bgw_someName.CancelAsync();
      }
}

results matching ""

    No results matching ""