I try to catch exceptions from another thread, but can't.
static void Main(string[] args)
{
try
{
Task task = new Task(Work);
task.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine();
}
public static void Work()
{
throw new NotImplementedException();
}
I write try-catch and at method too, but nothing happens.
Please,tell me how to know that exception throw?
Maybe you could show me some example code.
Your code may not raise the exception as the main method will executes too fast and the process will terminate before you got the exception
Here how it would look your code
static void Main(string[] args)
{
Task task = new Task(Work);
task.Start();
var taskErrorHandler = task.ContinueWith(task1 =>
{
var ex = task1.Exception;
Console.WriteLine(ex.InnerException.Message);
}, TaskContinuationOptions.OnlyOnFaulted);
//here you should put the readline in order to avoid the fast execution of your main thread
Console.ReadLine();
}
public static void Work()
{
throw new NotImplementedException();
}
Try to take a look at ContinueWith
The OnlyOnFaulted member of the TaskContinuationOptions enumeration
indicates that the continuation should only be executed if the
antecedent task threw an exception.
task.ContinueWith((Sender) =>
{
////This will be called when error occures
Sender.Result
}, TaskContinuationOptions.OnlyOnFaulted);
Your try/catch wouldn't work. For one reason : because you could very well have gone out of the try block before the exception is thrown, as the Task is done on another thread.
With a Task, there are two ways to get the exceptions.
The first one is to use task.Wait(); in your try block. This method will rethrow any exception thrown by the task.
Then, any exception will be handled on the calling thread in the catch block.
The second one is to use the ContinueWith method. This won't block your calling thread.
task.ContinueWith(t =>
{
// Here is your exception :
DoSomethingWithYour(t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
Note the following will block the main thread since Wait is employed.
try
{
Task task = Task.Factory.StartNew(Work);
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.ToString());
}
Related
Thread version results in unhandled exception, which crashes the app but the task version doesn't. Both are running exactly the same method Can someone explain the reason for this difference in exception behavior ?
Thread version:
try
{
new Thread(new ThreadStart(DoWork)).Start(); // do work throws exception
}
catch (Exception e)
{
Console.WriteLine(e);
}
static void DoWork()
{
Console.WriteLine("in thread");
throw new Exception();
}
Task version:
var errorTask = Task.Factory.StartNew<Func<string>>(() =>
{
Console.WriteLine("in task");
throw new Exception();
});
try
{
string result = errorTask.Result();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Thread.Start starts new thread, but you're handling exception in another thread:
try
{
// DoWork throws exception in new thread;
// threads don't catch exceptions out-of-the-box
new Thread(new ThreadStart(DoWork)).Start();
}
catch (Exception e)
{
// you're handling exception in "old" thread
Console.WriteLine(e);
}
Task.Factory.StartNew starts new task. Task catches exception inside it to set its Status property:
var errorTask = Task.Factory.StartNew<Func<string>>(() =>
{
Console.WriteLine("in task");
// this exception will be caught in Task's base code,
// since tasks catch exceptions thrown by task methods;
// note, that this will be wrapped into AggregateException
throw new Exception();
});
when you're trying to get Task.Result, and task is in faulted state, it just re-throws exception:
// this will re-throw exception in calling thread
string result = errorTask.Result;
That's why your second catch catches it.
To shed some light on the topic one could consult the documentation for Task.Result<TResult>() (or the one for Task.Wait() for what it's worth).
Under thrown exceptions (particularly AggregateException) is says
An exception was thrown during the execution of the task. The AggregateException.InnerExceptions collection contains information about the exception or exceptions.
A Task is kind of a managed thread (in very simple terms) which gives us some merits, e.g. this exception handling when accessing Result or Wait (or using await). On the other hand a Thread will execute separately from the method you are calling it from. You start the thread an (virtually) immediately leave the try / catch block. There is no way to know for the thread that there is an associated try / catch. Basically the thread does not know anything about the calling function. The other way round, if the calling function blocked its own thread to wait for the thread it created, just to make use of the try / catch this would basically render creating new threads useless.
I do not know how I should properly propagate an exception from a Task to the thread that created this task:
private void threadMT()
{
Task task;
try
{
task = new Task(() =>
{
throw new Exception("blabla");
});
task.Start();
while(!task.IsCompleted)
Thread.Sleep(500);
if (task.IsFaulted)
throw task.Exception;
}
catch (Exception ex)
{
throw ex;
}
}
When this line is reached:
throw new Exception("blabla");
the app halts saying that the exception is not handled.
Can it be propagated back to method?
Thx in advance.
The easiest way for you to propagate an exception from a Task executed on the thread-pool is to turn it to actually return a Task which you can await on:
public async Task AwaitOnTaskAsync()
{
try
{
await DoStuffWithThreadAsync();
}
catch (Exception e)
{
}
}
public Task DoStuffWithThreadAsync()
{
return Task.Run(() => { throw new Exception("blabla"); });
}
await will make sure to unwrap the exception out of the Task, allowing you to apply a try-catch on it.
Side Note - Don't use the Task constructor, instead use Task.Run to return a "hot task" (one which has already started). There's no point in creating a Task which you're actively blocking on using Thread.Sleep later on, either execute it synchronously or use async-await to asynchronously wait on the task.
Consider following scenario
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));
var startNew = Task.Factory.StartNew(() =>
{
var currentThread = Thread.CurrentThread;
try
{
using (cancellationTokenSource.Token.Register(currentThread.Abort))
new AutoResetEvent(false).WaitOne(Timeout.InfiniteTimeSpan);
}
catch (ThreadAbortException abortException)
{
throw new TimeoutException("Operation timeouted", abortException);
}
}, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
startNew.ContinueWith(val => Console.WriteLine("Cancellation handled"), TaskContinuationOptions.OnlyOnCanceled);
startNew.ContinueWith(val => Console.WriteLine("Fault handled"), TaskContinuationOptions.OnlyOnFaulted);
startNew.ContinueWith(val => Console.WriteLine("Ran to completion handled"), TaskContinuationOptions.OnlyOnRanToCompletion);
Putting aside all the discussion that aborting threads is evil, why does this code doesn't make the task to go to Faulted state? However removing catch block or calling
Thread.ResetAbort()
seems to do the trick
It is about how thread abort works:
try {
try {
try {
Thread.CurrentThread.Abort();
} catch(Exception e) {
Console.WriteLine(e.GetType());
throw new Exception();
}
} catch(Exception e) {
Console.WriteLine(e.GetType());
}
} catch(Exception e) {
Console.WriteLine(e.GetType());
}
This code prints:
System.Threading.ThreadAbortException
System.Exception
System.Threading.ThreadAbortException
So, when your custom exception would be handled, ThreadAbortException whould be re-thrown.
ThreadAbortException is a special exception that can be caught by application code, but is re-thrown at the end of the catch block unless ResetAbort is called. MSDN
Now let us look to some source:
/// <summary>
/// Executes the task. This method will only be called once, and handles bookeeping associated with
/// self-replicating tasks, in addition to performing necessary exception marshaling.
/// </summary>
private void Execute()
{
if (IsSelfReplicatingRoot)
{
ExecuteSelfReplicating(this);
}
else
{
try
{
InnerInvoke();
}
catch (ThreadAbortException tae)
{
// Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
// it's already been done downstream.
if (!IsChildReplica)
{
// Record this exception in the task's exception list
HandleException(tae);
// This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
// skip the regular Finish codepath. In order not to leave the task unfinished, we now call
// FinishThreadAbortedTask here.
FinishThreadAbortedTask(true, true);
}
}
catch (Exception exn)
{
// Record this exception in the task's exception list
HandleException(exn);
}
}
}
As you can see, there is special codepath for ThreadAbortException case to transit task to the faulted state. As you hide ThreadAbortException by TimeoutException, that special codepath are not taken. So when regular codepath handle exception by recording it in the task's exception list, ThreadAbortException would be re-thrown, which prevent correct task transition to the faulted state.
Here is my code:
private static Stopwatch _stopwatch;
static void PrintException(Exception ex)
{
Console.WriteLine(_stopwatch.Elapsed);
Console.WriteLine(ex);
}
static void ThrowException1()
{
throw new InvalidAsynchronousStateException();
}
static void ThrowException2()
{
throw new NullReferenceException();
}
static async Task ExecuteTask1()
{
await Task.Delay(1000);
ThrowException1();
}
static async Task ExecuteTask2()
{
await Task.Delay(2000);
ThrowException2();
}
static async Task Execute()
{
var t1 = ExecuteTask1();
var t2 = ExecuteTask2();
try
{
await t2;
}
catch (NullReferenceException ex)
{
// the NullReferenceException will be captured
Console.WriteLine("==============");
PrintException(ex);
}
}
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += (sender, ev) => PrintException(ev.Exception);
_stopwatch = Stopwatch.StartNew();
Execute();
while (true)
{
Thread.Sleep(5000);
GC.Collect();
}
}
Actually, I didn't await t1 in Execute method, but it seems it was still executed, since I captured the AggregateException about five seconds later.
Can someone tell me when the t1 was executed? In my case, the exceptions order which printed to the Console is 1. NullReferenceException 2. AggregateException
In the async/await world, tasks are "hot". So, when you call ExecuteTask1, the task returned to you is already being processed. It has already started at that point. You can put a Console.WriteLine at the beginning of ExecuteTask* to see that they do start immediately.
await is only used to (asynchronously) wait for the completion of a task. It does not start tasks.
I have an async intro on my blog that you may find helpful.
The task was executed at the point you called ExecuteTask1 here:
var t1 = ExecuteTask1();
You don't need to await a task for it to execute, it will run anyway... you await the task if you want your code to resume executing only after the task has finished otherwise your code will continue running immediately after the task has started running without waiting for it to finish.
If you do not await a task it will still execute, your current execution context will just not "wati for it".
This means you have no direct control over the task and if something goes wrong "inside the task" the exception will not directly propagate to your execution context as it would do when using await or t1.Wait().
In general exceptions which are thrown inside a task are boxed inside an AggregateException, so you can not do the following:
catch (NullReferenceException ex)
You need to do something like and check e.g. for the inner exception:
catch (AggregateException ex)
{
if(ex.InnerException is NullReferenceException)
// handle NRE
else
throw; // NOT "throw ex" to keep the stack trace
}
I'm trying to run the following code:
class Program
{
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
throw new ApplicationException("message");
});
try
{
task.ContinueWith(t => Console.WriteLine("End"));
}
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
}
}
I expected that the Exception would be caught in the following location:
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
But this is not happening. Why is this so?
You're just printing out the task - which won't even have completed yet.
Printing out the task doesn't wait for it to complete, or try to fetch the value.
If you change your code to:
try
{
task.Wait();
}
... then I'd expect it to catch the exception.
(I was previously using Task<T>.Result, but I notice this is a task with no return value, so it would just be the non-generic Task.)
The way Task works, the code that ends up calling the delegate you pass to StartNew will be caught, eventually, and the Exception will be stored in an instance field of the task. That exception can be inspected by looking at the task.Exception property. The line Console.WriteLine(task) is just calling task.ToString internally. That method won't result in the exception being thrown or re-thrown.
However, under certain circumstances the exception that is caught will be re-thrown. Two examples are when accessing Result and calling Wait, as well as when you await a task in C# 5.0.
The following code:
try
{
task.Wait();
}
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
Will result in the stored exception being re-thrown and the exception message will be printed.
AggregateException and ApplicationException are both children of the same class, System.Exception.
AggregateException is-not a ApplicationException
Cause your statement is not in the try, but before it... catch will catch every exception from within the try curly brackets...