Preventing Silent Thread Termination




Preventing Silent Thread Termination

Problem

An exception thrown in a spawned worker thread will cause this thread to be silently terminated if the exception is unhandled. You need to make sure all exceptions are handled in all threads. If an exception happens in this new thread, you want to handle it and be notified of its occurrence.

Solution

You must add exception handling to the method that you pass to the ThreadStart delegate with a try-catch, try-finally, or try-catch-finally block. The code to do this is shown in Figure in bold.

Preventing silent thread termination

using System;
using System.Threading;

public class MainThread
{
    public void CreateNewThread( )
    {
        // Spawn new thread to do concurrent work.
        Thread newWorkerThread = new Thread(Worker.DoWork);
        newWorkerThread.Start( );
    }
}

public class Worker
{
    // Method called by ThreadStart delegate to do concurrent work
    public static void DoWork ( )
    {
        try
        {
            // Do thread work here.
        }
        catch
        {
            // Handle thread exception here.
            // Do not re-throw exception.
        }
        finally
        {
            // Do thread cleanup here.
        }
    }
}

Discussion

If an unhandled exception occurs in the main thread of an application, the main thread terminates, along with your entire application. An unhandled exception in a spawned worker thread, however, will terminate only that thread. This will happen without any visible warnings, and your application will continue to run as if nothing happened.

Simply wrapping an exception handler around the Start method of the THRead class will not catch the exception on the newly created thread. The Start method is called within the context of the current thread, not the newly created thread. It also returns immediately once the thread is launched, so it isn't going to wait around for the thread to finish. Therefore, the exception thrown in the new thread will not be caught since it is not visible to any other threads.

If the exception is rethrown from the catch block, the finally block of this structured exception handler will still execute. However, after the finally block is finished, the rethrown exception is, at that point, rethrown. The rethrown exception cannot be handled and the thread terminates. If there is any code after the finally block, it will not be executed, since an unhandled exception occurred.

Never rethrow an exception at the highest point in the exception-handling hierarchy within a thread. Since no exception handlers can catch this rethrown exception, it will be considered unhandled and the thread will terminate after all finally blocks have been executed.


What if you use the THReadPool and QueueUserWorkItem? This method will still help you because you added the handling code that will execute inside the thread. Just make sure you have the finally block set up so that you can notify yourself of exceptions in other threads as shown earlier.

In order to provide a last-chance exception handler for your WinForms application, you need to hook up to two separate events. The first event is the System.appdomain.CurrentDomain.UnhandledException event, which will catch all unhandled exceptions in the current appdomain on worker threads; it will not catch exceptions that occur on the main UI thread of a WinForms application. See Recipe 7.10 for more information on the System.appdomain.UnhandledException event. In order to catch those, you also need to hook up to the System.Windows.Forms.Application.ThreadException, which will catch unhandled exceptions in the main UI thread. See Recipe 7.20 for more information about the THReadException event.

See Also

See the "Thread Class" and "Exception Class" topics in the MSDN documentation.