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 BackgroundWorker
and the outer code. Its tasks are the following:
- To join a
BackgroundWorker
with the array of integers and the sorting method.
- To provide the handlers for the events of the
BackgroundWorker
.
- To give a suitable interface to control a sorting thread.
- 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;
public event SortingExchangeEventHandler SortingExchange;
public event SortingCompleteEventHandler SortingComplete;
We will use obviously named methods to control the sorting thread (instead of generally named methods of BackgroundWorker
).
public void Execute()
{
worker.RunWorkerAsync(arrayToSort);
}
public void Stop()
{
worker.CancelAsync();
}
The constructor will set up the worker
completely
public BackgroundSorter(int[] array, SortMethod theMethod)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
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.
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");
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) MessageBox.Show(e.Error.Message);
OnSortingComplete(e.Cancelled);
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
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 SortModel
acts 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 ArraySortingView
to display the sorting process. A consideration of the class ArraySortingView
is out of this article.
The class SortController
encapsulates 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.
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)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
if (arrayToSort[j] > arrayToSort[j + 1])
{
worker.ReportProgress(j * 1000 + (j + 1));
System.Threading.Thread.Sleep(delay);
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"