Rusty
Rusty

Reputation: 13

BackgroundWorkers in BackgroundWorker - Error Handling

I have a BackgroundWorker that runs a lengthy and semi-complex task. In this BackgroundWorker, I have several other BackgroundWorkers that are being used within this "main worker".

All the code for the DoWork method is done, but I'm having some trouble with error management.

If an exception is thrown inside my main worker, it automatically gets caught and I can retrieve it in my WorkerCompleted method. However, if an exception is thrown in one of my "sub workers", it gets caught and returned to the main worker (SubWorkerCompleted).

Now to my problem: What do I do with this exception? What is the best way of reporting this up high so I can cancel the main worker?

My first thought was calling, in the sub workers Completed method, this.CancelAsync(). But then I can't see the difference between an exception in one of the sub workers and a user cancellation.

Implementing some kind of event in the main worker that is raised when sub worker exceptions occurs (so I can cancel the main worker) doesn't seem right somehow.

I also tried throwing an exception in the main worker with the sub worker exception as inner exception, but the main worker's Completed method was never called with the error. I assume this is because the sub worker's Completed method (which is inside the main worker) is raised on a different thread than the main worker?

The call hierachy now is basically: UI -> main worker -> does stuff and at the same time starts sub workers (that does stuff).

This is my first attempt at something more complex (I'm a newbie hobbyist), so any tips are appreciated.

Upvotes: 1

Views: 1217

Answers (3)

TheSean
TheSean

Reputation: 4566

My first guess is that you should be able to re-throw the excption in the sub-worker's completed event and this should get handled by the main BW and cause it's completed event to be raised.

However, I tried this out and it seems more difficult than this. The re-thrown exception is not caught by the main BW (as you've noticed). This is probably just another shortcoming of the BackgroundWorker class.

My suggestion is to build your own mechanism to handle the exception. You can cancel the main BW when an error happens on a sub BW, then pass the exception (or other user state) to the main BW using a member variable or something. (BW Completed event has an event arg for UserState, but there is no way to use this - another shortcoming)

Alternatively, this might be a good time to redesign your algorithm to only use one level of background workers. there may be other hidden features like this you will discover.

Upvotes: 0

Aaron McIver
Aaron McIver

Reputation: 24713

BackgroundWorker should typically be used for one-offs. Calling another BackgroundWorker from one BackgroundWorker is technically legit; yet it feels dirty and cumbersome. If you sincerely want to continue with this approach you can however; I would not recommend it.

In general any exception that is not caught within a BackgroundWorker will end the thread and propagate outward in a nice clean fashion as you mentioned. If you want to catch Exception you could analyze the exceptions and wrap them in a defined type that would suit your needs upstream. Then you can simply analyze the Exception type and do as you wish.

In going another route you could put a proxy in place that that mimics the Task behavior if you are not on .NET 4; however if you are on .NET 4 then leverage the Task class.

An MSDN article exists by Shawn Wildermuth which touches on varying ways to implement threading within a WPF application; one of which is listed below.

ThreadStart start = delegate()
{
    // ...

    // This will work as its using the dispatcher
    DispatcherOperation op = Dispatcher.BeginInvoke(
        DispatcherPriority.Normal, 
        new Action<string>(SetStatus), 
        "From Other Thread (Async)");

    DispatcherOperationStatus status = op.Status;
    while (status != DispatcherOperationStatus.Completed)
    {
        status = op.Wait(TimeSpan.FromMilliseconds(1000));
        if (status == DispatcherOperationStatus.Aborted)
        {
            // Alert Someone
        }
    }
};

// Create the thread and kick it started!
new Thread(start).Start();

Upvotes: 1

CodingGorilla
CodingGorilla

Reputation: 19842

Have you considered using 'System.Threading.Task' instead of BackgroundWorker for your "sub workers". Tasks have very nice exception handling and it might be a lot easier for you to use those to manage your sub-tasks and then handle it in your main BackgroundWorker.

Upvotes: 1

Related Questions