Reputation: 4786
I have a worker thread which, when it terminates, signals an event. This event is then marshalled over to the main thread to notifiy it of the worker thread's termination. When the worker thread encounters an unhandled exception, I want this exception to be handled by the main thread's error handling system. Therefor, the worker thread sets a property indicating its unexpected termination, and saves the exception in another property, then signals the event and exits.
After the event has been marshalled over to the main thread, I want to throw a new exception with the original exception set as the inner exception. My question is: what should the type of this new Exception be? Is there a specific System.somethingException for this kind of situation, should I Design my own Exception class for this specific situation, or would throwing a standard System.Exception with a proper message be considered appropriate?
C#-psuedo code:
class MyThread
{
public TerminationState Termination { get; private set; }
public Exception UncaughtException { get; private set; }
public delegate void ThreadTerminatedDelegate(MyThread thread);
public event ThreadTerminatedDelegate ThreadTerminated;
private void run()
{
try
{
doSomeWork();
}
catch(Exception e)
{
UncaughtException = e;
Termination = TerminationState.AbortOnException;
ThreadTerminated(this);
return;
}
Termination = TerminationState.NormalTermination;
ThreadTerminated(this);
}
}
class MainThread
{
private MyThread myThread = new MyThread();
private void run()
{
myThread.ThreadTerminated += handleTermination;
myThread.Start();
}
private void handleTermination(MyThread thread)
{
if (InvokeRequired)
{
MyThread.ThreadTerminatedDelegate cb = new MyThread.ThreadTerminatedDelegate(handleTermination);
BeginInvoke(cb, new object[] { thread });
}
else
{
if (thread.Termination == TerminationState.AbortOnException)
{
if (isFatal(thread.UncaughtException))
throw new Exception("", thread.UncaughtException); //what to do here?
else
fixTheProblem();
}
else
{
//normal wrapping up
}
}
}
}
Upvotes: 4
Views: 1337
Reputation: 3265
I believe that you can perform all necessary exception handling for unhandled background exceptions by performing the background work in a Task
and then handling any exceptions in a continuation of that task that is explicitly scheduled to run on the main thread. There are additional options you can specify for the continuation, but this should cover your scenario.
Task.Factory.StartNew(
() =>
{
// Do some work that may throw.
// This code runs on the Threadpool.
// Any exceptions will be propagated
// to continuation tasks and awaiters
// for observation.
throw new StackOverflowException(); // :)
}
).ContinueWith(
(a) =>
{
// Handle your exception here.
// This code runs on the thread
// that started the worker task.
if (a.Exception != null)
{
foreach (var ex in a.Exception.InnerExceptions)
{
// Try to handle or throw.
}
}
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
Another useful link is MSDN's Asyncronous Programming Patterns. It identifies the 3 main ways to implement asynchronous operations in an application. Your current implementation sounds most similar to what the article calls EAP (Event-based Asynchronous Pattern).
I personally prefer TAP (Task-based Asynchronous Pattern) which relies on the .NET 4.0 TPL (Task Parallel Library). It's well worth mastering due to the simplicity of its syntax and its extensive capabilities.
From MSDN:
Also, don't forget about the trusty BackgroundWorker
class. This class was a staple for me for a long time and although it has become somewhat deprecated by TAP, it will still get the job done and is very easy to understand and use.
// Create a new background worker.
var bgw = new BackgroundWorker();
// Assign a delegate to perform the background work.
bgw.DoWork += (s, e) =>
{
// Runs in background thread. Unhandled exceptions
// will cause the thread to terminate immediately.
throw new StackOverflowException();
};
// Assign a delegate to perform any cleanup/error handling/UI updating.
bgw.RunWorkerCompleted += (s, e) =>
{
// Runs in UI thread. Any unhandled exception that
// occur in the background thread will be accessible
// in the event arguments Error property.
if (e.Error != null)
{
// Handle or rethrow.
}
};
// Start the background worker asynchronously.
bgw.RunWorkerAsync();
Upvotes: 1