RSS 2.0

Personal Info:

Joe Send mail to the author(s) leads the architecture of an experimental OS's developer platform, where he is also chief architect of its programming language. His current mission is to enable writing large-scale software that is reliable, secure, and scalable by-construction. Before this, Joe founded the Parallel Extensions to .NET project. He has been granted 19 patents, with 49 pending. When not working, Joe enjoys travelling with his wife, writing books, writing music, studying music theory & mathematics, and doing anything involving food & wine.

My books

My music

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2012, Joe Duffy

 
 Friday, December 17, 2004

I was looped into a question from a customer today regarding how to best handle exceptions generated by subordinate threads from within the owning thread. They were using threads as a mechanism to do a set of primary tasks in parallel. By primary I mean these aren't just actions that disappear into the background to be executed "at some time in the future," but rather had normal scheduler priority and were responsible for the main workload of the application.

There are plenty of situations where this is a good practice, for example when you need to continually refresh the UI to notify the user of progress. Moreover, if calculations are highly independent and one or more might block and/or if you're on a multi-processor machine, it's often useful to split tasks into many concurrent mini-programs, kick them off, and simply have the parent join on them.

Anyhow, the customer's primary question was around the best way to propagate exceptions back to the owning thread. Truthfully, there isn't a single great way to do this, and it highly depends on your scenario. I did, however, give a simple code snippet of one thought on how to abstract the process of doing this. I figured somebody out there might also find it useful.

The basic idea is to create a thread worker class which encapsulates thread execution and handling of any subsequent errors and calculated values. E.g.

public class ThreadWorker<T>

{

 

    // fields

 

    private T workerReturnValue;

    private Exception workerException;

    private Thread workerThread;

    private ThreadWorkerStart<T> workerStart;

 

    // ctors

 

    public ThreadWorker(ThreadWorkerStart<T> start)

    {

        workerStart = start;

    }

 

    // properties

 

    public T WorkerReturnValue

    {

        get { return workerReturnValue; }

    }

 

    public Exception WorkerException

    {

        get { return workerException; }

    }

 

    // methods

 

    public void Start()

    {

        workerThread = new Thread(Worker);

        workerThread.Start();

    }

 

    public T Join()

    {

        workerThread.Join();

        if (workerException != null)

            throw new Exception("Worker threw exception", workerException);

        return workerReturnValue;

    }

 

    private void Worker()

    {

        try

        {

            workerReturnValue = workerStart();

        }

        catch (Exception e)

        {

            workerException = e;

        }

    }

 

}

The Join() method here will either re-throw the exception generated in the ThreadWorkerStart method, or return the calculated value if no exception was generated. This enables you to handle it in the parent thread. Admittedly, the only case you want to do this is when the parent thread can take some corrective action and/or re-run the thread entirely. Most of the time, you want to try and handle exceptions as locally as possible, i.e. in the worker start method itself. However, if the worker thread is unable to do so, letting it leak is sometimes the right thing to do.

For example, this snippetdemonstrates both scenarios:

Random r = new Random();

for (int i = 0; i < 15; i++)

{

    ThreadWorker<int> t = new ThreadWorker<int>(delegate()

    {

        int value = r.Next();

        if ((value % 3) == 0)

            throw new Exception("Uh oh, something bad happened");

        else

            return value;

    });

    t.Start();

 

    try

    {

        Console.WriteLine("{0}. Worker output: {1}", i, t.Join());

    }

    catch (Exception ex)

    {

        Console.WriteLine("{0}. Worker exception: {1}", i, ex.InnerException.ToString());

    }

}

Here, we generate, execute, and join on 15 threads, each of which will throw an exception should a random number be divisible by 3. Our code will print out either the computed value or the exception depending on whether the worker method throws an exception or not.

12/17/2004 10:00:58 PM (Pacific Standard Time, UTC-08:00)  #   

 

Recent Entries:

Search:

Browse by Date:
<December 2004>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

Browse by Category:

Notables: