Reputation: 11146
I have an object A from which i want to execute new thread with object's B method. I can use Task.CreateNew etc.. problem is i don't know how to handle exception in new thread.
Generally what i want is that inner thread with object's B method to throw exception which parent object A will catch and close it's execution, along with object B.
i cannot add code to master loop at all
catching exception after master loop done is no acceptable, i want to catch inner thread exception on time it occurs
Is there any way to achieve it ?
In code below i don't have exception handling and master thread continues:
static void Main()
{
Console.WriteLine("start");
Task.Factory.StartNew(PrintTime, CancellationToken.None);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("inner thread i={0}",i+1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}
Upvotes: 1
Views: 1011
Reputation: 25464
The below solution works regardless which thread throws an exception at first:
static void Main()
{
Console.WriteLine("start");
var innerCts = new CancellationTokenSource();
Exception mainException = null;
var mainThread = new Thread(() => SafeMainThread(innerCts, ref mainException));
mainThread.Start();
var innerTask = Task.Factory.StartNew(state => PrintTime(state),
innerCts,
innerCts.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
var innerFault = innerTask.ContinueWith(t => { Console.WriteLine("Inner thread caused " + t.Exception.InnerException.GetType().Name + ". Main thread is being aborted..."); mainThread.Abort(); },
TaskContinuationOptions.OnlyOnFaulted);
var innerCancelled = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread cancelled."),
TaskContinuationOptions.OnlyOnCanceled);
var innerSucceed = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread completed."),
TaskContinuationOptions.OnlyOnRanToCompletion);
try
{
innerTask.Wait();
}
catch (AggregateException)
{
// Ignore.
}
mainThread.Join();
Console.ReadLine();
}
private static void SafeMainThread(CancellationTokenSource innerCts, ref Exception mainException)
{
try
{
MainThread();
Console.WriteLine("Main thread completed.");
}
catch (ThreadAbortException)
{
Console.WriteLine("Main thread aborted.");
}
catch (Exception exception)
{
Console.WriteLine("Main thread caused " + exception.GetType().Name + ". Inner task is being canceled...");
innerCts.Cancel();
mainException = exception;
}
}
private static void MainThread()
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(500);
}
throw new Exception("exception");
}
private static void PrintTime(object state)
{
var cts = (CancellationTokenSource)state;
for (int i = 0; i < 10; i++)
{
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("inner thread i={0}", i + 1);
Thread.Sleep(500);
}
throw new Exception("exception");
}
Upvotes: 1
Reputation: 25464
If you can't modify internals of main body, I don't know how to get fine-grained control on it. The solution I see at the moment is to wrap the main body into managed thread that allows to abort it if inner thread throws an exception:
static void Main()
{
Console.WriteLine("start");
var mainThread = new Thread(MainThread);
mainThread.Start();
var task = Task.Factory
.StartNew(PrintTime)
.ContinueWith(t => { Console.WriteLine("Inner thread caused exception. Main thread is being aborted."); mainThread.Abort(); },
TaskContinuationOptions.OnlyOnFaulted);
task.Wait();
Console.WriteLine("Waiting for main thread to abort...");
mainThread.Join();
Console.WriteLine("Main thread aborted.");
Console.ReadLine();
}
private static void MainThread()
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("inner thread i={0}", i + 1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}
Upvotes: 0
Reputation: 2533
You can try and run an exception handler on the occurrence of exception. You can use a flag, I have used the exception for checking the exception occurence itself in the example:
private static AggregateException exception = null;
static void Main(string[] args)
{
Console.WriteLine("Start");
Task.Factory.StartNew(PrintTime, CancellationToken.None).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("Master Thread i={0}", i + 1);
Thread.Sleep(1000);
if (exception != null)
{
break;
}
}
Console.WriteLine("Finish");
Console.ReadLine();
}
private static void HandleException(Task task)
{
exception = task.Exception;
Console.WriteLine(exception);
}
private static void PrintTime()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Inner Thread i={0}", i + 1);
Thread.Sleep(1000);
}
throw new Exception("exception");
}
Upvotes: 0
Reputation: 54917
Retain a reference to your task instance, and call Wait
on it when you're ready to process its result. Any unhandled inner exception that was thrown during the task's execution would be wrapped in an AggregateException
that is, in turn, thrown from the Wait
method.
static void Main()
{
Console.WriteLine("start");
Task task = Task.Factory.StartNew(PrintTime, CancellationToken.None);
for (int i = 0; i < 20; i++)
{
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
// Stop iterating in case of unhandled exception in inner task.
if (task.Status == TaskStatus.Faulted)
break;
}
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
Console.WriteLine("Exception: " + x.ToString());
});
}
}
Upvotes: 2
Reputation: 25464
Try to intercept inner exception and check its value in the main loop as if would be a cancellation request:
static void Main()
{
Console.WriteLine("start");
try
{
AggregateException innerException = null;
Task.Factory.StartNew(PrintTime, CancellationToken.None)
.ContinueWith(t => innerException = t.Exception, TaskContinuationOptions.OnlyOnFaulted);
for (int i = 0; i < 20; i++)
{
if (innerException != null)
throw innerException;
Console.WriteLine("master thread i={0}", i + 1);
Thread.Sleep(1000);
}
}
catch (AggregateException)
{
Console.WriteLine("Inner thread caused exception. Main thread handles that exception.");
}
}
Upvotes: 0