Click here to Skip to main content
Click here to Skip to main content

Dealing with Progressive Operations

, 10 May 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Through a clean OOP solution to deal with progressive operations, I will implicitly show you how OOP principles can work together to make a full, clean solution.

Introduction

This is not the first article I write in my life, but this is the first one I will publish here in the CodeProject community and, also, this is the first article which I am writing in English. Let's start from the beginning: Hello everybody, I hope you like this article and, please, try not to be too hard with me and my English.

When I was thinking about what could be the theme of my article, I remembered my days when I was a newcomer to object oriented programming and the troubles I had understanding object oriented programming principles. I mean, I did understand what these principles were in theory, but taking them to practice was a totally different issue. Almost every time, I ended up using similar tools and techniques I had used before, when programming in ANSI C language.

No, I am not going to write another article speaking about the trite theme of OOP principles with simple code snippets showing separately how each one of these principles comes in handy. Instead, I will bring up a little problem to solve, and then I will make it grow step by step. I have really enjoyed my time writing these words and, as I said before, I hope you enjoy reading them.

Background

The background requirements to follow this article will keep growing as long as you keep reading it. The very first part is really simple and can be followed by any beginner, but maybe you will need something else to completely understand the last one. Anyway, I have tried to keep the samples as simple as possible. However, I will assume you know some theoretical basics about OOP, C# syntax and lambda expressions. The last part is totally related to multithreading execution.

First of all, be aware that the purpose of this article is just didactic. It is not a complete library/solution, so you will find it lacks some features that might be necessary in a real world production environment. Nevertheless, I think you can find here a good point of start that can help you deal with some of the most common problems we usually face as programmers.

The Problem to Solve

We have developed a simple application and, while testing, we have noticed that as data keeps growing, our application, although it keeps working and performing well, sometimes it seems to be blocked due to some of the processes it has to execute are some seconds long. Yes, we can explain to our users that they should not make Ctrl+Alt+Supr and just keep waiting for the process to finish but... how can I tell you this... Have you ever tried to explain anything to a user?

Ok, just kidding, there is an issue with our user interface. The application should show something to the user, in order to make him feel it is doing its job and he just has to wait. A modal Form with a progress bar looks like a good solution but we must do it carefully, because there are several different processes which will need this Form, so it has to be useful for all of them.

Process and User Interface Should Not Be Tied

There are many possible approaches to face a simple problem like this, but there is something you can never forget: process and user interface should not be tied. If a class, for example, has to make a backup of an amount of files, it should just notify how the process is going on to "anyone who wants to know". It needs to know nothing about your user interface, because it does not mind if you are showing a progress bar, or a movie or nothing at all. On the other hand, your user interface does not have to know anything about what process is going on. All it needs to know is how it is progressing.

There is one more thing to consider: each and every progressive operation has some common things: they start, progress and finish. Maybe some of them can throw exceptions, maybe some others could do something else but all of them start, progress and finish. I know it is quite obvious but I have included this to explain why I have designed this interface:

public interface IProgressiveOperation
{
    event EventHandler OperationStart;
    event EventHandler OperationProgress;
    event EventHandler OperationEnd;

    string MainTitle { get; set; }

    string SubTitle { get; set; }

    int CurrentProgress { get; }
    int TotalSteps { get; }
    int CurrentStep { get; }

    void Start();
}

This is the main idea: If we implement IProgressiveOperation interface in all of the classes that in fact perform progressive operations, it will be really easy to show the progress of all of them in our user interface, whichever it is. For example, I will show the progress in a Windows Form with a progress bar and a couple of labels, one like this one:

And this is all the code we need to implement for it to work:

internal partial class frmModalProgressForm : Form
{
    internal frmModalProgressForm(IProgressiveOperation operation)
    {
        InitializeComponent();

        // Subscribe to the operation events
        operation.OperationStart += (sender, e) =>
            {
                lblTitle.Text = operation.MainTitle;
                lblSubtitle.Text = operation.SubTitle;
                pgb.Value = operation.CurrentProgress;

                Refresh();
            };

        operation.OperationProgress += (sender, e) =>
            {
                pgb.Value = operation.CurrentProgress;

                Application.DoEvents();
            };

        operation.OperationEnd += (sender, e) => Close();

        // Subscribe to Shown event of the Form
        Shown += (sender, e) => operation.Start();
    }
}

The Form receives an IProgressiveOperation object in the constructor and subscribes to its events. This means it receives any object of any class which implements this interface. That is all what our user interface needs to know about the real operation that will take place: here you might make a backup of a set of files, or compact a database, or just play a music file. Actually, we have not implemented any IProgressiveOperation yet, but we are free to design our user interface because we know exactly how these operations will behave. If you implement IProgressiveOperation in the classes where your program performs those jobs, the user interface will be able to show their progress.

The rest is quite straightforward: when the form is shown (i.e. Shown event of the Form is raised) it starts the operation; and when the operation raises its events the Form just reacts as needed, updating the ProgressBar value, the labels, etc. and closing itself on OperationEnd event. Even more, like this simple Form might be useful for a good amount of applications, I will include it in my utilities library. This way, any Windows Forms application I develop will be able to handle progressive operations without any complication. Still more, I like things simple, so I will make this Form internal and provide a static class (which I will name Lib) with a static method to show it:

public static void StartProgressiveOperation(
    IProgressiveOperation operation,
    IWin32Window owner)
{
    frmModalProgressForm f =
        new frmModalProgressForm(operation);

    f.ShowDialog(owner);
}

Yes, I know, having to implement this interface on each and every class which performs a progressive operation might be a pain in the neck but, hey!, I've not finished yet...

Make the Idea Grow

So far we have seen the main idea and I think we all agree it is a good idea. Now let's make it grow, step by step; maybe we can convert this idea into a good tool for us. We have seen a tiny IProgressiveOperation. We should also include some code for handling exceptions, but we will not do it in order to keep things simple in this article.

Provide a Default Implementation for IProgressiveOperation

IProgressiveOperation interface requires some elements to be implemented. These elements are related to the code required to raise the events and control the percentage completed of the overall process. The best thing is that these elements will be common for a vast majority of the progressive operations we need to implement, so making a common base class makes good sense. Here it is:

public abstract class BaseProgressiveOperation:IProgressiveOperation
{
    string _title = "Processing";
    string _subtitle = "Please wait";

    protected int _totalSteps;
    protected int _currentStep;

    protected virtual void OnOperationStart(EventArgs e)
    {
        if (OperationStart != null)
            OperationStart(this, e);
    }

    protected virtual void OnOperationProgress(EventArgs e)
    {
        if (OperationProgress != null)
            OperationProgress(this, e);
    }

    protected virtual void OnOperationEnd(EventArgs e)
    {
        if (OperationEnd != null)
            OperationEnd(this, e);
    }

    #region IProgressiveOperation Members

    public event EventHandler OperationStart;

    public event EventHandler OperationProgress;

    public event EventHandler OperationEnd;

    public virtual string MainTitle
    {
        get { return _title; }
        set { _title = value; }
    }

    public virtual string SubTitle
    {
        get { return _subtitle; }
        set { _subtitle = value; }
    }

    public virtual int CurrentProgress
    {
        get
        {
            int ret=0;

            if (_totalSteps > 0)
                ret = (100 * _currentStep) / _totalSteps;

            return ret;
        }
    }

    public virtual int TotalSteps
    {
        get { return _totalSteps; }
    }

    public virtual int CurrentStep
    {
        get { return _currentStep; }
    }

    public abstract void Start();

    #endregion
}

Of course, it is an abstract class and its members are virtual. I like to be able to modify the behaviour of the classes I inherit in case I need it. When you inherit this class, you will only have to take care of a few things: initialize and modify the values of _totalSteps and _currentStep fields and make the calls to OnOperationStart, OnOperationProgress and OnOperationEnd methods when necessary, and, of course, override the abstract method Start.

Let's illustrate this point with a sample. We will implement a class which will change an image to its negative. Remember this is just a sample. Actually, what you are going to see now is not the best way to manage images in .NET (in fact, maybe it is the worst way to do it...). I have done it this way for two reasons: I needed a process with a good number of steps and I needed it to be slow enough for you to see the progress window working. If you want to manage image pixels, you should use the BitmapData object returned by the LockBits method in Bitmap class. So, please, do not focus your attention on the work done to the pixels; just look at how I am inheriting BaseProgressiveOperation and interacting with its base implementation.

class NegativePO : BaseProgressiveOperation
{
    Bitmap _bmp;

    public NegativePO(Bitmap bmp)
    {
        _bmp = bmp;
        _totalSteps = bmp.Width * bmp.Height;

        MainTitle = "Performing negative transformation";
    }

    public override void Start()
    {
        _currentStep = 0;
        OnOperationStart(EventArgs.Empty);

        for (int x = 0; x < _bmp.Width; x++)
        {
            for (int y = 0; y < _bmp.Height; y++)
            {
                Color c = _bmp.GetPixel(x, y);
                Color n = Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B);
                _bmp.SetPixel(x, y, n);

                _currentStep++;
                OnOperationProgress(EventArgs.Empty);
            }
        }

        OnOperationEnd(EventArgs.Empty);
    }
}

You see, it is not hard to take advantage of BaseProgressiveOperation implementation of IProgressiveOperation interface. Still more, with this implementation you are free to change the way your user interface will show the progress to the users as many times as you want. Whatever you do with your user interface, you will not have to change anything in the NegativeProgressiveOperation class, i.e., your worker class. As a result, it will not be too hard to reuse the classes you implement for your progressive operations, as anyone could expect for a class designed for just doing a well defined process, but that is not all: you can also reuse the part of the user interface which shows the progress of your progressive processes.

Looping the Loops

Though I am a loyal supporter of the KISS principle (have a look at Wikipedia if you want to know more about KISS), I have to admit I need to loop the loops from time to time... Who has never wondered: what happens if...? We as programmers usually need some challenges to face and, sometimes, we succumb to the temptation of converting a simple problem into a complex challenge. Yes, I admit it is one of my worst faults, but sometimes that tendency has driven me through steep paths which, at the end, led me to even more KISS compliant solutions than the ones that made me begin to loop the loops.

Why am I telling you this? Well, the fact is that the world is not simple, and still less simple is our profession. Coming back to the scope of this article, what happens when we have a progressive operation composed of some "smaller" progressive operations? We should be able to show the progress of each "smaller" operation and, at the same time, the overall progress of all of them. I am sure you have seen this behaviour in lots of programs. Our problem here is that IProgressiveOperation interface and BaseProgressiveOperation do not provide the necessary tools for this behaviour.

Sure we are facing a more complex problem right now but the first thing we have to wonder is: ok, how much complex? Let's get to it step by step: maybe we need a new interface but, since a composite progressive operation is a progressive operation itself, we should inherit this interface from IProgressiveOperation. So, we just have to add the elements to give access to the current sub-operation executing and notify when a new operation is going to take place. This is the resulting interface:

public interface ICompositeProgressiveOperation : IProgressiveOperation
{
    IProgressiveOperation CurrentOperation { get; }

    event EventHandler NewOperation;
}

There are, of course, many other possibilities, but I think this is the most KISS compliant. NewOperation event should be raised just before a new operation is going to be started, and CurrentOperation property should return the last operation being processed since the last NewOperation event notification.

Making a good default implementation for this interface is not hard, but it is not as straightforward as we might think at first glance. Like our CompositeProgressiveOperation class is a progressive operation itself, it should inherit from BaseProgressiveOperation and, of course, should implement ICompositeProgressiveOperation. We will need a List<IProgressiveOperation> to host the operations we will have to process, and a reference to the one which is being processed at the time. Let's have a look at the easiest part of this class, and we will see the trickiest part a little later:

public class CompositePO :
    BaseProgressiveOperation, ICompositeProgressiveOperation
{
    protected List<IProgressiveOperation> _operations;
    protected IProgressiveOperation _currentOperation;

    protected CompositePO() { }

    public CompositePO(List<IProgressiveOperation> operations)
    {
        _operations = operations;

        _totalSteps = _operations.Sum<IProgressiveOperation>(po => po.TotalSteps);
    }

    // ... Start method near here...

    protected virtual void OnNewOperation(EventArgs e)
    {
        if (NewOperation != null)
            NewOperation(this, EventArgs.Empty);
    }

    #region ICompositeProgressiveOperation Members

    public virtual IProgressiveOperation CurrentOperation
    {
        get { return _currentOperation; }
    }

    public event EventHandler NewOperation;

    #endregion
}

Nothing exciting with this part, I know... just what is required to track the operations, current operation and raising the NewOperation event. One important thing to consider, however, is that _totalSteps of this class will be the sum of steps of all operations to execute: I want to raise the OperationProgress event whenever my sub-operations raise their OperationProgress event. That was the trickiest part (not that tricky in fact...) and that is what we are implementing within the Start method. Here it is:

public override void Start()
{
    _currentStep = 0;
    OnOperationStart(EventArgs.Empty);

    foreach (IProgressiveOperation po in _operations)
    {
        _currentOperation = po;

        po.OperationProgress +=
            (sender, e) =>
            {
                _currentStep++;
                OnOperationProgress(EventArgs.Empty);
            };

        OnNewOperation(EventArgs.Empty);

        po.Start();
    }

    OnOperationEnd(EventArgs.Empty);
}

We just have to process the operations in a foreach loop, subscribing to the OperationProgress of each one and launching the OperationProgress event of the CompositeProgressiveOperation every time one of the sub-operations raises its OperationProgress event. But remember: the classes we have seen are not thread safe. They are designed for using them on just one thread.

The moment to test if this CompositePogressiveOperation in a user interface has come, and it is now when things become much easier than what we expected. Our first impulse would be to subscribe this form to the events of CompositeProgressiveOperation object for the overall progress bar and then subscribe to the events of each IProgressiveOperation every time we receive a NewOperation event. Just don't follow your impulses, but think about it for a while. Because of the way we have designed these classes, our CompositeProgressiveOperation will raise OperationProgress event on every OperationProgress events of its children, so we just have to listen to the CompositeProgressiveOperation events to have each part into its place with a minimum effort. Let's see it all in the next form:

As you see, this is a simple Form with a couple of progress bars. The upper one will show us the overall progress of the CompositeProgressiveOperation, and the other one will show the progress of the current operation taking place. Now I will show you the minimum code required to put all of this together, and then you can play the game "Find the differences" comparing this code with the one provided for the first Form sample given in this article, related to the simple IProgressiveOperation interface.

public partial class frmModalCompositeProgressForm : Form
{
    public frmModalCompositeProgressForm(
        ICompositeProgressiveOperation operation)
    {
        InitializeComponent();

        // Subscribe to operation events
        operation.NewOperation += (sender, e) =>
            {
                lblCurrentComponent.Text =
                    operation.CurrentOperation.MainTitle;

                Refresh();
            };

        operation.OperationStart += (sender, e) =>
            {
                lblTitle.Text = operation.MainTitle;
                lblSubtitle.Text = operation.SubTitle;

                Refresh();
            };

        operation.OperationProgress += (sender, e) =>
            {
                pgbOverallProgress.Value = operation.CurrentProgress;
                pgbCurrentProgress.Value = operation.CurrentOperation.CurrentProgress;

                Application.DoEvents();
            };

        operation.OperationEnd += (sender, e) => Close();

        // Subscribe to Shown event of the form
        Shown += (sender, e) => operation.Start();
    }
}

Both forms are pretty similar, huh? Yes, I love KISS principle! We can trace every operation just subscribing to the CompositeProgressiveOperation events. There is one more thing to do: let's add a new static method to our static Lib class:

public static void StartProgressiveOperation(
    ICompositeProgressiveOperation operation,
    IWin32Window owner)
{
    frmModalCompositeProgressForm f =
        new frmModalCompositeProgressForm(operation);

    f.ShowDialog(owner);
}

Ok, this is beautiful but, here comes again... What happens if...

Threads are in Town...

So long, what we have seen can be very useful on many situations when we have to execute tasks which have to be run in the same thread of the main application. The examples used to show you how this works can also give you a clue on when to use them: transforming a picture seems a very good candidate for a progressive operation that must be executed under the main application thread, I mean, the main application cannot continue until this operation has finished.

But there are cases where the operation can perfectly be executed on its own thread and the main application does not have to wait until it gets finished. Figure out, for example, we want to add to an application the capability of finding files which contain some text. Sure, in this case the search can perfectly be done in the background, so that a modal form showing its progress does not make sense at all. We are again in front of a more complex problem but, of course, how much complex?

It is usually a common mistake to treat every complication of a problem as if they were new problems, but many times they are not, and this is a good example. IProgressiveOperation interface and BaseProgressiveOperation implementation keep on being handy without any changes. We just have to be careful when overriding Start method, because this time the class we develop must be thread safe.

However, let's get to it step by step. The first thing we need is another interface. Yes, I know I have just said that IProgressiveOperation was still handy, and yes, it is, as a base for our new interface. Our basic IProgressiveOperation lacks some features that could be useful for a class which has to search files, for it to notify when a file matches the search criteria, for example.

interface IFindInFilesPO : IProgressiveOperation
{
    event FileFoundEventHandler FileFound;

    List<string> ErrorsInFiles { get; }

    string SearchPath { get; }
    string SearchText { get; }
}

You see, not a very exotic interface. Just a new event to notify when a file has been found and some practical readonly properties. The ErrorsInFiles property will just return a list with the full path of the files which failed due to some reason, but it will not give any further information. As we are dealing here with the file system, I don't want a file system error breaking my operation. We could improve this part a lot but, remember, I want to keep things simple in this article. If you want to use this, or something similar, on your own projects, I guess you will have to make some changes with the exception handling part.

FileFoundEventHandler is just a new delegate to pass some arguments when raising the FileFound event. Here you are FileFoundEventArgs and FileFoundEventHandler code:

class FileFoundEventArgs : EventArgs
{
    public readonly string FileName;

    public FileFoundEventArgs(string file)
    {
        FileName = file;
    }
}

public delegate void FileFoundEventHandler(object sender, FileFoundEventArgs e);

Now we can implement IFindInFilesPO. Maybe some of you are wondering why I have defined IFindInFilesPO and have not implemented directly the FindInFilesPO class. Well... by now take it as a hunch... but something tells me it is better to do it with an interface (I mean, you will find out the reason as you keep on reading).

I am going to focus now on the most important part of FindInFilesPO class. I guess you will be able to figure out the rest of it and, if you can't, you can download it. I will begin with the Start method:

public override void Start()
{
    if (!Monitor.TryEnter(this))
        throw new Exception(
            "Operation is already running");

    _currentStep = 0;
    OnOperationStart(EventArgs.Empty);

    foreach (string file in _files)
    {
        StreamReader sr = null;
        bool found = false;

        try
        {
            sr = new StreamReader(file);
            while (!sr.EndOfStream && !found && !AbortRequired)
            {
                string line = sr.ReadLine();
                found = line.Contains(_text);
            }

            if (found)
                OnFileFound(new FileFoundEventArgs(file));

            if (AbortRequired)
            {
                OnAborted(EventArgs.Empty);
                return;
            }

            _currentStep++;
            OnOperationProgress(EventArgs.Empty);
        }
        catch (ThreadAbortException)
        {
            OnAborted(EventArgs.Empty);
            return;
        }
        catch
        {
            lock (_errors)
                _errors.Add(file);
        }
        finally
        {
            if (sr != null)
                sr.Close();
        }
    }

    Monitor.Exit(this);

    OnOperationEnd(EventArgs.Empty);
}

There are three very important points where you must pay attention here: the operation cannot be started again while it is running and has not finished yet. That is the reason for Monitor.TryEnter at the beginning of the method, and Monitor.Exit at the end. On the other hand, we have to catch a possible ThreadAbortException because it will be thrown if the thread where the class is running is aborted. What we do here is just raising our Aborted event (we will see IAbortable interface later). When we have an unhandled exception, it will be captured on the second catch, adding the file path to the _errors collection. And this is the third important point: we must lock the collection before accessing it, because we have a property which returns its contents and this property might be called from a different thread. AbortRequired is a private property which will indicate just what you guess... abort is required. It is here because there are two ways to abort the operation: by aborting the thread where it is running or calling the Abort method of the class. And now, how can we implement ErrorsInFiles in a thread safe way? Easy, just lock the collection, make a copy of it and return the copy:

public List<string> ErrorsInFiles
{
    get
    {
        List<string> lst = null;

        lock (_errors)
            lst = new List<string>(_errors);

        return lst;
    }
}

Now, running this operation on a background thread is a child's play, but you have to be careful to avoid cross-threading exceptions when updating your user interface with events coming from background threads, I mean, be sure you use Invoke method to make those updates. Imagine, for example, we have an IProgressiveOperation object named _po which will be running on a separate thread, and we want to update the value of a ProgressBar on _po OperationProgress event. This could be the code to avoid cross-threading exceptions with our user interface:

void _po_OperationProgress(object sender, EventArgs e)
{
    if (InvokeRequired)
        Invoke(new EventHandler(_po_OperationProgress), sender, e);
    else
        pgb.Value = _po.CurrentProgress;
}

Remember to do something like this whenever you have to update a component of your user interface from a different thread than the one which created that component (usually the main application thread).

Ok, we have seen how to make a simple IProgressiveOperation which can be executed on its own thread but, once again, what happens if... I feel this would not be complete if I just speak about single thread safe progressive operations. What happens if a CompositePO runs on its own thread and wants to run each one of its children on their own threads?

This is becoming more and more interesting. It might look pretty easy to achieve that behaviour: just put a thread pool and queue the calls to each Start method in your list of progressive operations. Unfortunately, that will not be enough. Managed ThreadPool lacks some features which we really need and will not perform well for our intensive working progressive operations. Anyway, I think that implementing a progressive operation which will use the managed ThreadPool can help me to figure out what it lacks. This way, I will have a better picture of what I need.

public class BackgroundCompositePO_ThreadPool :
    BaseProgressiveOperation
{
    Mutex _curStepMutex;
    protected List<IProgressiveOperation> _operations;

    protected BackgroundCompositePO_ThreadPool()
    {
        _curStepMutex = new Mutex(false);
    }

    public BackgroundCompositePO_ThreadPool(
        List<IProgressiveOperation> operations) : this()
    {
        _operations = operations;
        _totalSteps = _operations.Sum<IProgressiveOperation>(po => po.TotalSteps);
    }

    public override void Start()
    {
        if (!Monitor.TryEnter(this))
            throw new InvalidOperationException(
                "Operation is already running");

        _currentStep = 0;
        OnOperationStart(EventArgs.Empty);

        foreach (IProgressiveOperation po in _operations)
        {
            po.OperationProgress +=
                (sender, e) =>
                {
                    IncreaseCurrentStep();
                    OnOperationProgress(EventArgs.Empty);
                };

            ThreadPool.QueueUserWorkItem(new WaitCallback(InitOperation), po);
        }
    }

    void InitOperation(object operation)
    {
        ((IProgressiveOperation)operation).Start();
    }

    protected void IncreaseCurrentStep()
    {
        _curStepMutex.WaitOne();
        _currentStep++;

        if (_currentStep == _totalSteps)
        {
            OnOperationEnd(EventArgs.Empty);
            Monitor.Exit(this);
        }

        _curStepMutex.ReleaseMutex();
    }
}

What? Why did I not inherit from CompositePO? Yes, I know that it looks like the most accurate design but, believe me, it is not. Sure this class will execute a collection of operations, but they will be executed on parallel threads. That's why NewOperation event and CurrentOperation property have nothing to do with this class, and that's the reason why I have inherited from BaseProgressiveOperation instead. As you see, we just queue the execution of each operation into the ThreadPool and do not worry about how or when the operations will be executed. We give that task to the ThreadPool. On the other hand, we are using a Mutex to safely increase the _currentStep counter, just to avoid that several threads try to increase this variable at the same time, causing it to hold bad data.

However, as I stated before, there are some major problems with this implementation. ThreadPool works pretty well for short threads which spend most of their time just sleeping, waiting for an event or a signal to a WaitHandle, but does not perform well when the threads take a long time and have a high activity, and does not provide any further control over the threads running, so there is no way, for example, to abort a thread started with the ThreadPool. Likewise it is true that this implementation we have done using the managed TheadPool looks quite beautiful due to its simplicity. I would like to improve this without adding too much complexity to this basic implementation. Will I be able to do so? I don't know... let's see.

... And Threads Have Come to Stay

Once again, there are many possible approaches. We could control the threads within the BackgroundCompositePo class itself, or we could use one of the many customized ThreadPool implementations available on the Internet. You can find some of them right here, on CodeProject. I don't like the first approach at all, because that design would not allow me to reuse the part of the code deployed to manage the threads. The second approach seems much better, and I am sure it is, but... hey! I am a hopelessly minimalist. Many of the customized implementations for thread pools we can find out there seem to be very complete, but right now I do not need such complete things. I prefer to develop a tiny solution which gives me no more than what I need right now... I'm sure that more "What happens if..." will come to my mind after that, sooner or later...

A Tiny AbortableThreadPool Implementation

Then, what I need is a thread pool class which allows me to create instances of it, abort all of the threads of one instance and abort all of the threads of every instance. Considering these requirements, my AbortableThreadPool will have two sides: one will be static, which will be responsible for limiting the total number of threads that can be executed at the same time (no matter how many instances have been created) and allow me to create instances and abort the execution of all running instances. The other side must be the instance one, I mean, the implementation that will allow me to create a thread pool and abort its running threads. This way, my BackgroundCompositePO class will be abortable and... wait... The thread pool must be abortable... and the BackgroundCompositePO must be abortable... I am sure you can guess what I am going to do next:

public interface IAbortable
{
    event EventHandler Aborted;

    void Abort();
}

Yes, I am a hopeless minimalist, but I also like to have each thing on its place... Both things must always remain in a good balance... Ok, let's get back to our AbortableThreadPool. This is its static part:

public sealed class AbortableThreadPool :
    IAbortable, IDisposable
{
    #region Static part

    const int MaxSimultaneousThreads = 25;
    const int TimeoutMilliseconds = 10;

    static Semaphore _semaphore;
    static List<AbortableThreadPool> _instances;

    static TimeSpan _timeout;

    static AbortableThreadPool()
    {
        _semaphore = new Semaphore(MaxSimultaneousThreads, MaxSimultaneousThreads);
        _instances = new List<AbortableThreadPool>();

        _timeout = TimeSpan.FromMilliseconds(TimeoutMilliseconds);
    }

    public static AbortableThreadPool NewInstance()
    {
        return NewInstance(MaxSimultaneousThreads);
    }

    public static AbortableThreadPool NewInstance(int concurrentThreads)
    {
        if (concurrentThreads > MaxSimultaneousThreads || concurrentThreads < 1)
            throw new ArgumentException();

        AbortableThreadPool pool = new AbortableThreadPool();
        pool._maxThreadsRunning = concurrentThreads;

        lock(_instances)
            _instances.Add(pool);

        return pool;
    }

    public static void AbortAll()
    {
        AbortableThreadPool[] array = new AbortableThreadPool[_instances.Count];

        lock (_instances)
            _instances.CopyTo(array);

        foreach (AbortableThreadPool pool in array)
            pool.Abort();
    }

    #endregion

    // ... next is the instance part

There are many interesting points to pay attention here so, please, be patient. First of all is the constant MaxSimultaneousThreads. Sure I could have done it variable in order to make this number customizable but, remember, this is just a sample, and I think 25 simultaneous threads is a good number to keep a good balance between performance and multithread overhead. This number indicates the maximum number of threads which we will be able to run simultaneously, and it does not depend at all on the number of instances we create of our AbortableThreadPool. I mean, if we create five instances of AbortableThreadPool and sum the number of threads running simultaneously on all of them, this number will never be greater than MaxSimultaneousThreads. We will use the _semaphore object to make sure that thread execution will not exceed this number (have a look at the _semaphore initialization in the static constructor).

The List<AbortableThreadPool> will contain a reference to all instances of this class which have been created and have not been disposed. When you invoke the NewInstance method, it will create an instance of it and save a reference to it into the _instances list. When we examine the Dispose method in the instance part of the implementation, we will see that it will remove itself from the _instances list. This collection of instances will allow us to abort all of the executing thread pools (see AbortAll method). As you see, we are always locking the _instances object before trying to access it.

The last thing to comment is the static _timeout field. This will be used when trying to acquire the _semaphore prior to executing a new thread, I mean, it is the maximum time it will wait for acquiring the _semaphore. It will be also used as the time interval that the timer responsible for running the threads will wait between its Elapsed event executions.

And now, we will examine the instance implementation little by little. We will begin with the instance fields, constructor and destructor:

object _lock;

int _maxThreadsRunning;
Dictionary<int, Thread> _runningThreads;
Queue<ThreadStart> _waitingCallbacks;

System.Timers.Timer _tmr;
bool _isDisposed = false;

private AbortableThreadPool()
{
    _runningThreads = new Dictionary<int, Thread>();
    _waitingCallbacks = new Queue<ThreadStart>();

    _lock = new object();

    _tmr = new System.Timers.Timer(_timeout.TotalMilliseconds);
    _tmr.Elapsed += new System.Timers.ElapsedEventHandler(_tmr_Elapsed);

    _tmr.Start();
}

~AbortableThreadPool()
{
    lock (_lock)
    {
        if (_runningThreads.Count != 0 || _waitingCallbacks.Count != 0)
            Abort();

        _isDisposed = true;
        _tmr.Dispose();
    }
}

First of all, to avoid confusion, _maxThreadsRunning determines the maximum number of threads this concrete pool will run at the same time and, of course, cannot be greater than MaxSimultaneousThreads. This might be useful if we want to limit the number of threads for a concrete thread pool despite the global MaxSimultaneousThreads defined limit.

Here the _runningThreads dictionary will only contain the threads which are running and have not finished their job jet. In other words, if a thread is running, it is sure we have a reference to it in the _runningThreads dictionary, and if it is not running, sure we do not have a reference to it in the _runningThreads dictionary.

The _waitingCallbacks queue will just enqueue the delegates which the pool will have to invoke, each one on its own thread. As we will see later, every time Elapsed event of the _tmr object occurs, if all of the necessary conditions are favourable, it will dequeue a callback and execute it on a new thread.

We also need a destructor, because a System.Timers.Timer must be disposed when we don't need it any more.

How do we add an operation to the pool? Just call AddOperation method (see it below), passing it the delegate to the method that has to be invoked. I have used here the simple ThreadStart delegate but, of course, it is not a "must be".

public void AddNewOperation(ThreadStart callback)
{
    CheckDisposed();

    lock (_lock)
        _waitingCallbacks.Enqueue(callback);
}

CheckDisposed method is just a private helper method responsible for throwing an ObjectDisposedException if you try to make some kind of operation on a disposed AbortableThreadPool. You see, adding a new callback is as simple as just enqueue it in _waitingCallbacks (sure, do not forget to make the lock before).

Now we will look at the trickiest part of this class, the Elapsed event handler for the timer and the RunThread method:

void _tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    lock (_lock)
    {
        _tmr.Stop();

        // Check for waiting callbacks
        if (_waitingCallbacks.Count != 0)
        {
            if (_runningThreads.Count < _maxThreadsRunning)
            {
                // I can run a new callback if _semaphore is free
                if (_semaphore.WaitOne(_timeout))
                {
                    ThreadStart callback = _waitingCallbacks.Dequeue();

                    Thread th = new Thread(
                        new ParameterizedThreadStart(RunThread));
                    th.IsBackground = true;

                    _runningThreads.Add(th.ManagedThreadId, th);

                    th.Start(callback);
                }
            }
        }

        if (!_isDisposed)
            _tmr.Start();
    }
}

void RunThread(object obj)
{
    ThreadStart callback = obj as ThreadStart;

    // Just make the callback
    callback();

    // When callback is done, just remove the thread
    lock (_lock)
        _runningThreads.Remove(Thread.CurrentThread.ManagedThreadId);

    // And signal the semaphore
    _semaphore.Release(1);
}

First of all, we must stop the timer to avoid overlapped executions of Elapsed event handler. I like to keep things simple and I do not want this situation to occur. After acquiring the lock, see how, if we acquire the semaphore, we are not directly creating a thread to immediately run the callback. Instead, we make the thread execute RunThread method. This way, we can handle it better, and remove the thread form _runningThreads dictionary just after the callback is done (remember, some exception handling code here might be necessary if you want to use this on your own projects). Finally, we cannot forget releasing the semaphore when a thread is finished. If you want to use something like this on your own projects, you might want to add some extra features to the AbortableThreadPool, like adding a new parameter for ThreadPriority to the AddOperation method, for example.

Disposing an AbortableThreadPool instance means you cannot use that instance for anything else, and aborting it means you will abort all of the running threads and, after that, the instance is also disposed. This way, the Aborted event can be raised just once, so the method to dispose the AbortableThreadPool must also take care of this requirement. Let's see the real worker Dispose method:

void Dispose(bool cleanAbortedEvent)
{
    lock (_lock)
    {
        if (!_isDisposed)
        {
            if (_waitingCallbacks.Count != 0 || _runningThreads.Count != 0)
                throw new InvalidOperationException("The thread pool is still running");

            GC.SuppressFinalize(this);

            _tmr.Dispose();
            _isDisposed = true;

            lock (_instances)
                _instances.Remove(this);

            if (cleanAbortedEvent)
                Aborted = null;
        }
    }
}

One important thing is that an AbortableThreadPool cannot be disposed if it has running threads or callbacks waiting to execute. Having that in mind, there is nothing special with this method. When we make a call to the public Dispose() method of this class, it will call its private method Dispose(true). We need this because Abort method will also dispose the pool but, after disposing it, it also has to raise the Aborted event. I mean, when Aborted event is received, the pool must have already been disposed. This is it:

public void Abort()
{
    CheckDisposed();

    lock (_lock)
    {
        // Clear the waiting callbacks queue
        _waitingCallbacks.Clear();

        // And abort all of the running threads
        foreach (Thread th in _runningThreads.Values)
        {
            // Abort the thread
            th.Abort();
            // And signal the semaphore
            _semaphore.Release(1);
        }

        _runningThreads.Clear();

        // Dispose the pool
        Dispose(false);

        // Raise Aborted event
        if (Aborted != null)
            Aborted(this, EventArgs.Empty);

        // Clean Aborted event
        Aborted = null;
    }
}

As expected, we clear the _waitingCallbacks queue and abort each running thread releasing the semaphore. After that, we dispose the pool without cleaning the Aborted event because we will need this delegate to raise this event. And, well, here it is, we already have a tiny AbortableThreadPool, and we can go on with our background composite progressive operations.

Give Me a Point of Support and...

Yes, my little AbortableThreadPool will be my point of support for the rest of the article. Now we can develop our BackgroundCompositePO. It will be very similar to the one we made using the managed thread pool, but be careful: the differences are really critical. To begin with the most obvious one, this class implements IAbortable interface. I will need, of course, an instance of the AbortableThreadPool class and I will subscribe to its Aborted event so, when it occurs on the pool, I will raise the Aborted event on the BackgroundCompositePO. I guess you already figured out these changes but there is one more, and it is really an important one. Let's see some code before speaking about it:

protected void IncreaseCurrentStep()
{
    _curStepMutex.WaitOne();
    _currentStep++;

    if (_currentStep == _totalSteps)
    {
        OnOperationEnd(EventArgs.Empty);
        Thread th = new Thread(new ThreadStart(DisposePool));
        th.Start();
    }

    _curStepMutex.ReleaseMutex();
}

void DisposePool()
{
    bool disposed = false;
    while (!disposed)
    {
        try
        {
            _pool.Dispose();
            disposed = true;
        }
        catch { Thread.Sleep(10); }
    }
}

Yes, we are launching a new thread to just dispose the pool. I know it looks like an unnecessary excess, but let me explain to you why this is absolutely necessary. IncreaseCurrentStep method will be invoked as a result of receiving an OperationProgress event from our children operations. This means that, in the last step of the whole process (_currentStep and _totalStep are equal), IncreaseCurrentStep method will be running on the same thread where the last child operation is running and, remember, we cannot abort the thread pool if there is any running thread. For that reason, we have to dispose the pool on another thread.

And now, I am wondering... How have we come to this point? Oh, yes, we showed a FindInFilesPO which could work on its own thread, and we wanted to make a CompositeFindInFilesPO which could run several FindInFilesPO at the same time, and then we thought about the managed thread pool and found out it was not enough, and then we decided to develop an abortable thread pool and made a BackgroundCompositePO based on this thread pool.

Well, I think we already have all of the tools we need for our CompositeFindInFilesPO. This will be a BackgroundCompositePO that will run one or several IFindInFilesPO operations, each one on its own thread. However, CompositeFindInFilesPO is also an IFindInFilesPO itself, so we will have to implement this interface for this class. There is not much to do for this class, due to which we have almost everything done in our base BackgroundCompositePO class. Let's see it:

class CompositeFindInFilesPO :
    BackgroundCompositePO, IFindInFilesPO
{
    string _searchText;
    string _searchPath;

    List<string> _errors;

    public CompositeFindInFilesPO(
        List<IFindInFilesPO> operations,
        string searchText, string searchPath)
        : base()
    {
        _searchText = searchText;
        _searchPath = searchPath;

        _errors = new List<string>();

        _operations = new List<IProgressiveOperation>();
        foreach (IFindInFilesPO op in operations)
        {
            op.FileFound += (sender, e) => OnFileFound(e);

            op.OperationEnd += (sender, e) =>
                {
                    if (op.ErrorsInFiles.Count != 0)
                    {
                        lock (_errors)
                            _errors.AddRange(op.ErrorsInFiles);
                    }
                };

            _operations.Add(op);
        }

        _totalSteps = _operations.Sum<IProgressiveOperation>(po => po.TotalSteps);
    }

    protected virtual void OnFileFound(FileFoundEventArgs e)
    {
        if (FileFound != null)
            FileFound(this, e);
    }

    public string SearchText
    {
        get { return _searchText; }
    }

    public string SearchPath
    {
        get { return _searchPath; }
    }

    #region IFindInFilesPO Members

    public event FileFoundEventHandler FileFound;

    public List<string> ErrorsInFiles
    {
        get
        {
            List<string> lst;

            lock (_errors)
                lst = new List<string>(_errors);

            return lst;
        }
    }

    #endregion
}

As I have just said, there is not much to do with this class. We just have to subscribe to each FileFound and OperationEnd events of the children operations and, as you see, the rest is child's play.

How to Use This

You can find the use of these classes if you download the source code attached to this article. You will see that using them is really an easy task. I will, however, show you some key parts of the code in this article.

To begin with one of them, as you have seen, each FindInFilesPO object can make the search on just one directory. How can we build a list of objects of this class in order to include the sub-directories? That is a job of a simple recursive method:

void FillOperationList(List<IFindInFilesPO> lst, string path)
{
    if (Directory.GetFiles(path).Length > 0)
    {
        IFindInFilesPO subOperation = new FindInFilesPO(txtSearchText.Text, path);
        lst.Add(subOperation);

        subOperation.OperationStart += onSubOperationStartInvoker;
        subOperation.OperationEnd += onSubOperationEndOrAbortInvoker;
        subOperation.Aborted += onSubOperationEndOrAbortInvoker;
    }

    string[] dirs = Directory.GetDirectories(path);
    foreach (string dir in dirs)
        FillOperationList(lst, dir);
}

I think there is not much to say about this method. It builds the FindInFilesPO for the "path" folder and then recursively calls itself for each one of the directories found in the "path" directory. Let's see how we should invoke this method, if required, before starting a new search:

private void btnStart_Click(object sender, EventArgs e)
{
    if (btnStart.Text == "Abort")
        _po.Abort();
    else
    {
        lstFiles.Items.Clear();
        lstFolders.Items.Clear();

        lblStatus.Text = "Preparing to search";
        ChangeControlsEnabled(false);

        Thread th = new Thread(new ThreadStart(PrepareToSearch));
        th.Start();
    }
}

void ChangeControlsEnabled(bool enable)
{
    txtSearchPath.Enabled = enable;
    txtSearchText.Enabled = enable;
    btnBrowse.Enabled = enable;
    btnStart.Enabled = enable;
    chkIncludeSubFolders.Enabled = enable;
}

void PrepareToSearch()
{
    if (!chkIncludeSubFolders.Checked)
        _po = new FindInFilesPO(txtSearchText.Text, txtSearchPath.Text);
    else
    {
        List<IFindInFilesPO> lst = new List<IFindInFilesPO>();
        FillOperationList(lst, txtSearchPath.Text);

        _po = new CompositeFindInFilesPO(lst, txtSearchText.Text, txtSearchPath.Text);
    }

    _po.FileFound += onFileFoundInvoker;
    _po.OperationStart += onStartInvoker;
    _po.OperationProgress += onProgressInvoker;
    _po.OperationEnd += onEndInvoker;
    _po.Aborted += onAbortInvoker;

    _po.Start();
}

As you will have already guessed, _po is declared as an IFindInFilesPO. This way we can instantiate a FindInFilesPo or a CompositeFindInFilesPO, depending on which one is required. Let's see now, for example, the OperationStart and FileFound event handlers for the _po object:

void _po_OperationStart(object sender, EventArgs e)
{
    if (InvokeRequired)
        Invoke(onStartInvoker, sender, e);
    else
    {
        btnStart.Text = "Abort";
        btnStart.Enabled = true;
        pgb.Visible = true;
    }
}

void _po_FileFound(object sender, FileFoundEventArgs e)
{
    if (InvokeRequired)
        Invoke(onFileFoundInvoker, sender, e);
    else
        lstFiles.Items.Add(e.FileName);
}

It is very important here to be aware of InvokeRequired value, in order to avoid cross threading exceptions, because we cannot change the controls from different threads than the ones which created them.

And now, here it comes again... what happens if...? Ok, guy, maybe some day in the future... by now I think it is enough.

Summary

We have seen how, starting from a simple problem with a simple solution, we can make our software grow, adding features little by little, just reusing our older solutions and making them grow.

I hope from now on, you will appreciate interfaces for what they really are if you did not before: interfaces are one of the most useful, important and powerful tools which object oriented programming provides us with. We have seen how inheritance can help us if we can distinguish what behaviour is common for a set of objects and should be implemented in the base class, and what behaviour is not.

We have seen a way to run background threads and how to make changes in the user interface avoiding cross-threading exceptions, and we have seen a very simple implementation of a customized thread pool, which you can make grow on your own to use in your own projects.

And just one more thing before finishing; remember, as you make your software grow, you will also grow as a programmer.

History

  • 10th May, 2010: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

_Erik_
Technical Lead
Spain Spain
No Biography provided

Comments and Discussions

 
GeneralVery well written Pinmembermicahs12-May-10 16:08 
GeneralRe: Very well written Pinmember_Erik_13-May-10 6:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411019.1 | Last Updated 10 May 2010
Article Copyright 2010 by _Erik_
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid