Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Covering a BackgroundWorker by an Own Class to Lighten the Creation of a Multithread Application

0.00/5 (No votes)
6 Aug 2015 2  
The article describes an application that executes several different sorting algorithms on the arrays of integers in the separate threads and graphically visualizes them. The application main thread interacts with the sorting threads using the special C# class that encapsulates a BackgroundWorker.

Introduction

The application has three reasons to be useful. Firstly, it takes a chance to observe the execution of several sorting processes simultaneously. Secondly, it shows how we can cover a BackgroundWorker by an own class in purpose to automate the set up of it. Thirdly, it is an example implementation of the MVC pattern.

Background

Some time ago I used to use a nice training example from the Borland Delphi tutorial - thrddemo - to teach my students how to create a multithread application with the help of the VCL TThread objects. The main advantage of that example was a simple usage of the TThread descendant classes to start the different sorting algorithms in the separate threads. Now I teach C# and need something similar written for the .Net framework. The decision is to apply the BackgroundWorker instances. But a straightforward usage of them leads to a huge amount of a duplicated code. Then I found a helpful tip on codeproject.com in the article "Working with BackgroundWorker & Creating Your Own Loading Class" by Hieuuk and decided to create an own class - BackgroundSorter - to carry out all rough work in it. Now I want to describe the results of my work.

Requirements to the Application

Our goal is to build the multithread application. Its main thread would maintain the GUI and interact with four background threads. Each of them would sort an array of integers by a known sorting algorithm and display every exchange of the array elements on the window of the application. The array would appear on the window as a column of segments. The length of each segment would be proportional to the corresponding element of the array. A user of the application would be able to generate the new arrays before the sorting starts, to start the sorting threads and to terminate any of them ahead of time.

We will use an instance of BackgroundWorker to control a sorting thread. Let it be named bw. Let's recall what we have to do in order to set up it before using. Firstly, we have to define a sorting method and assign it to bw.DoWork event. Then we have to set the properties bw.WorkerSupportsCancellation and bw.WorkerReportsProgress to true and assign a handler method to bw.ProgressChanged event. This event will inform us about the array elements exchanges. And finally, we need to set up a handler to bw.RunWorkerCompleted event.

Naturally, the sorting method must be arranged in a special way so that it can interact with the bw.  It must check the bw.CancellationPending property to break the execution on demand and it should also call bw.ReportProgress() every time an exchange of values is done.

Design of BackgroundSorter

Our new class will act as a mediator between a BackgroundWorkerand the outer code. Its tasks are the following:

  1. To join a BackgroundWorker with the array of integers and the sorting method.
  2. To provide the handlers for the events of the BackgroundWorker.
  3. To give a suitable interface to control a sorting thread.
  4. To inform the outer code about the important events in the sorting thread.

So, data members of the BackgroundSorter can look like follows

public class BackgroundSorter
{
    private BackgroundWorker worker;
    private int[] arrayToSort;
    private SortMethod sortMethod;
    // Events affecting the view:
    // - exchange of two array elements
    public event SortingExchangeEventHandler SortingExchange;
    // - completion of the sorting process
    public event SortingCompleteEventHandler SortingComplete;
    // ...

We will use obviously named methods to control the sorting thread (instead of generally named methods of BackgroundWorker).

    // access to the backgroundWorker interface
    public void Execute()
    {
        worker.RunWorkerAsync(arrayToSort);
    }
    public void Stop()
    {
        worker.CancelAsync();
    }

The constructor will set up the workercompletely

public BackgroundSorter(int[] array, SortMethod theMethod)
    {
        worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.WorkerSupportsCancellation = true;
    // Set-up required methods to interact with the backgroundWorker:
    // - main long term work
        worker.DoWork += worker_DoWork;
    // - action after main work completion
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
    // - displaying of main work progress
        worker.ProgressChanged += worker_ProgressChanged;

        this.arrayToSort = array;
        this.sortMethod = theMethod;
    }

Where worker_DoWork, worker_RunWorkerCompleted and worker_ProgressChanged are the methods of the BackgroundSorter. First of them will call the sorting method, second one will check if the worker finished without errors and will inform the application about the sorting completion, third one will translate a "progress changed" message to terms of integers exchange.

// The backgroundWorker events handlers:
    // - long term execution - the array sorting
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        if (sortMethod != null && arrayToSort != null)
        {
            sortMethod((int[])e.Argument, sender as BackgroundWorker, e);
        }
        else throw new NullReferenceException("Trying to use null array or null sorting method");
    }
    // - handler of completion of the sorting process
    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null) MessageBox.Show(e.Error.Message);
        OnSortingComplete(e.Cancelled);
    }
    // - handler of sorting progress reports indexes of exchanged elements
    private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // the elements indexes are packed in the property ProgressPercentage
        int i = e.ProgressPercentage / 1000;
        int j = e.ProgressPercentage % 1000;
        OnSortingExchange(i, j);
    }

Note that the argument of ProgressChanged event can report a single integer but we need at least two ones: indexes of the array elements that have to be exchanged. A simple way to overcome this contradiction is to pack the indexes into a number as follows number=firstIndex​*1000+secondIndex​. It will work with any array not bigger then a thousand of elements.

The complete version of BackgroundSorter you can find in the download files. 

The Application Architecture

Let's briefly describe the entire application. It is built according to the MVC pattern.

The class SortModelacts as the model. It contains four identical arrays and provides the methods for their initialization. The class VisualForm describes the main window of the application and uses four instances of the user control ArraySortingViewto display the sorting process. A consideration of the class ArraySortingViewis out of this article.

The class SortControllerencapsulates four instances of BackgroundSorter and uses the static methods of class SortMethodsProvider. Each such method is designed to sort an array and to interact with a background worker simultaneously. Let's take a look at one of them. The interaction part is market with bold.

// Bubble sort algorithm (simple exchange method)
public static void BubbleSortInBackground(int[] arrayToSort, BackgroundWorker worker, DoWorkEventArgs e)
{
    for (int i = arrayToSort.Length - 1; i > 0; --i)
        for (int j = 0; j < i; ++j)
        {
            // Cancel request checking
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
            // Looking for pairs of neighboring elements situated in wrong order
            if (arrayToSort[j] > arrayToSort[j + 1])
            {
                // Exchange vizualization in the main thread
                worker.ReportProgress(j * 1000 + (j + 1));
                System.Threading.Thread.Sleep(delay);
                // The exchange: to correct a "wrong pair" of elements
                int t = arrayToSort[j];
                arrayToSort[j] = arrayToSort[j + 1];
                arrayToSort[j + 1] = t;
            }
        }
}

The application class diagram is shown below. All code of the application is in the download files.

Class diagram of the application "Sorting in threads"

Let's look at a screenshot of the running application. Two of four sorting threads are finished and two others are still working.

Running application "Sorting in threads"

License

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