Problem
Several tasks are run in parallel, and all, none, or any of them might throw exceptions. When all the tasks have finalized, all the exceptions that might have happened must be reported (via log, email, console output.... whatever).
Expected behavior
I can build all the tasks via linq with async lambdas, and then await for them running in parallel with Task.WhenAll(tasks). Then I can catch an AggregateException and report each of the individual inner exceptions.
Actual behavior
An AggregateException is thrown, but it contains just one inner exception, whatever number of individual exceptions have been thrown.
Minimal complete verifiable example
static void Main(string[] args)
{
try
{
ThrowSeveralExceptionsAsync(5).Wait();
}
catch (AggregateException ex)
{
ex.Handle(innerEx =>
{
Console.WriteLine($"\"{innerEx.Message}\" was thrown");
return true;
});
}
Console.ReadLine();
}
private static async Task ThrowSeveralExceptionsAsync(int nExceptions)
{
var tasks = Enumerable.Range(0, nExceptions)
.Select(async n =>
{
await ThrowAsync(new Exception($"Exception #{n}"));
});
await Task.WhenAll(tasks);
}
private static async Task ThrowAsync(Exception ex)
{
await Task.Run(() => {
Console.WriteLine($"I am going to throw \"{ex.Message}\"");
throw ex;
});
}
Output
Note that the output order of the "I am going to throw" messages might change, due to race conditions.
I am going to throw "Exception #0"
I am going to throw "Exception #1"
I am going to throw "Exception #2"
I am going to throw "Exception #3"
I am going to throw "Exception #4"
"Exception #0" was thrown
That's because await "unwraps" aggregate exceptions and always throws just first exception (as described in documentation of await), even when you await Task.WhenAll which obviously can result in multiple errors. You can access aggregate exception for example like this:
var whenAll = Task.WhenAll(tasks);
try {
await whenAll;
}
catch {
// this is `AggregateException`
throw whenAll.Exception;
}
Or you can just loop over tasks and check status and exception of each.
Note that after that fix you need to do one more thing:
try {
ThrowSeveralExceptionsAsync(5).Wait();
}
catch (AggregateException ex) {
// flatten, unwrapping all inner aggregate exceptions
ex.Flatten().Handle(innerEx => {
Console.WriteLine($"\"{innerEx.Message}\" was thrown");
return true;
});
}
Because task returned by ThrowSeveralExceptionsAsync contains AggregateException we thrown, wrapped inside another AggregateException.
Related
I have an ActionBlock that simply processes messages that comes from an infinite loop continuously. Inside ActionBlock I make an http post. When on any network related error, the method throws exception and the block is faulted/stopped. This is not the behavior that I want. I want processing runs even though exception occurs. (Keep hitting Process method) To simulate my program;
private static ExecutionDataflowBlockOptions processBlockOptions
{
get
{
return new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 1
};
}
}
static async Start()
{
processQueue = new
ActionBlock<QueueMessage>(
async (item) =>
{
await Process(item);
},
processBlockOptions);
while (!Stopped)
{
//Read from DB and do logic with item
QueueMessage item= new QueueMessage();
await processQueue.SendAsync(item);
}
}
private async static Task<int> Process(QueueMessage item)
{
try
{
await item.HttpPost(order);
}
catch (Exception ex)
{
//Http endpoint might be broken
throw ex;
}
}
You're re-throwing your exceptions, and you're doing it wrong:
throw ex;
If you need to log the errors or stop the pipeline for a while, you don't need to throw anything, simply log ex.ToString() and react accordingly. Second thing is that you should use throw; instead of throw ex;, as you're rewriting the stack trace for exception, which doesn't really matter in this case but can be misleading in case of more complicated workflow.
In the method below, when an exception is thrown in the TRY block, it is being swallowed. How can I make it throw the exception so that it gets written to log in the catch block? The log writer works fine. Thanks!
public static bool MonitorQueueEmptyTask(string queueName, CancellationTokenSource tokenSource)
{
try
{
Task<bool> task = Task.Factory.StartNew<bool>(() =>
{
while (!QueueManager.IsQueueEmpty(queueName))
{
if (tokenSource.IsCancellationRequested)
{
break;
}
Thread.Sleep(5000);
throw new Exception("Throwing an error!"); //THIS THROW IS SWALLOWED -- NO LOG WRITTEN ON CATCH
};
return true;
}, tokenSource.Token);
}
catch (Exception ex)
{
WriteExceptionToLog(ex.Stack); //it's not that this method doesn't work. it works fine.
return false;
}
return true;
}
If you want to fire and forget, you can attach a continuation using ContinueWith. The current try-catch will not help you at all, as the exception is encapsulated inside the Task. If this is "fire and forget", than you can log the exception:
public static Task MonitorQueueEmptyTask(
string queueName, CancellationTokenSource tokenSource)
{
return Task.Factory.StartNew<bool>(() =>
{
while (!QueueManager.IsQueueEmpty(queueName))
{
if (tokenSource.IsCancellationRequested)
{
break;
}
Thread.Sleep(5000);
throw new Exception("Throwing an error!");
};
}, tokenSource.Token, TaskCreationOptions.LongRunning).ContinueWith(faultedTask =>
{
WriteExceptionToLog(faultedTask.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
This, in turn, will not propagate the exception after it's thrown, but will provide a mechanism to log the error. If you want the exception to be properly handled, you can register to TaskScheduler.UnobservedTaskException. Additionally, you can set ThrowUnobservedTaskExceptions enabled="true" in your configuration if you want unhandled exceptions to terminate your application. ContinueWith will consider the exception "handled" once you look at the task.Exception property.
The exception is not swallowed; it's just that it doesn't occur on the thread that executes the try/catch block, but on the separate Task thread.
If you don't observe the task's result or exception, when the task is eventually garbage collected, it will throw an exception saying that the task was not observed. Unless you catch that by handling the TaskScheduler.UnobservedTaskException, it will crash the process.
I also had a problem with this, and i really dislike the whole idea of App.config, so can provide another solution to prevent the exceptions disappearing :)
Save the exception then throw it after the Task.Run has completed, e.g.
private async void Function() {
Exception save_exception = null;
await Task.Run(() => {
try {
// Do Stuff
} catch (Exception ex) {
save_exception = ex;
}
}).ContinueWith(new Action<Task>(task => {
if (save_exception != null)
throw save_exception;
// Do Stuff
}));
}
i am using the following
Task.Factory.StartNew(() => DoPrintConfigPage(serial));
then the function i am calling looks like this
private void DoPrintConfigPage(string serial)
{
//do printing work
}
My problem is an exception is being thrown inside the thread and not being handled.
I have tried wrapping it in a try catch
try
{
Task.Factory.StartNew(() => DoPrintConfigPage(serial));
}
catch (Exception ex) { }
but it still is not catching the error and thus crashing the application.
How can I catch exceptions in the main thread so I can handle them?
Update
I have made the changes recommended below and still it is saying the exception is unhandled
var task = Task.Factory.StartNew(() => DoPrintConfigPage(serial))
.ContinueWith(tsk =>
{
MessageBox.Show("something broke");
},TaskContinuationOptions.OnlyOnFaulted);
then in my DoConfigPage I added another try catch.
In this catch is now where it is crashing and saying the exception being thrown was unhandled, what am I missing?
private void DoPrintConfigPage(string serial)
{
try
{
//call the print function
}
catch (Exception ex)
{
throw ex; //it is crashing here and saying it is unhandled
}
}
I also tried what Eric J. suggested with the same results
var task = Task.Factory.StartNew(() => DoPrintConfigPage(serial));
try
{
task.Wait();
}
catch (AggregateException ex) { MessageBox.Show("something broke"); }
Alternatively, you can chain your task creation and add a ContinueWith:
var job = Task.Factory
.StartNew(...)
.ContinueWith(tsk =>
{
// check tsk for exception and handle
});
EDIT: This snippet, when run, pops up the message box for me:
void Main()
{
var serial = "some serial";
var task = Task.Factory
.StartNew(() => DoPrintConfigPage(serial))
.ContinueWith(tsk =>
{
MessageBox.Show("something broke");
var flattened = tsk.Exception.Flatten();
// NOTE: Don't actually handle exceptions this way, m'kay?
flattened.Handle(ex => { MessageBox.Show("Error:" + ex.Message); return true;});
},TaskContinuationOptions.OnlyOnFaulted);
}
public void DoPrintConfigPage(string serial)
{
throw new Exception("BOOM!");
}
Your try block is exited right after you start the new task, because that method just continues to run.
Instead you can catch the Exception as an AggregateException where you wait for the task (or multiple tasks) to complete:
var task1 = Task.Factory.StartNew(() =>
{
throw new MyCustomException("I'm bad, but not too bad!");
});
try
{
task1.Wait();
}
catch (AggregateException ae)
{
// Assume we know what's going on with this particular exception.
// Rethrow anything else. AggregateException.Handle provides
// another way to express this. See later example.
foreach (var e in ae.InnerExceptions)
{
if (e is MyCustomException)
{
Console.WriteLine(e.Message);
}
else
{
throw;
}
}
}
http://msdn.microsoft.com/en-us/library/dd997415.aspx
If you are not waiting on your task, I think the easiest solution is found in Task.Exception:
Gets the AggregateException that caused the Task to end prematurely.
If the Task completed successfully or has not yet thrown any
exceptions, this will return null.
I am using something like this:
Task.Factory.StartNew(() => DoStuffHere())
.ContinueWith(task =>
{
if (task.Exception != null)
Log("log all the exceptions!");
});
You should also know about
System.Threading.Tasks.TaskScheduler.UnobservedTaskException.
If you are in the business of creating "fire and forget" Task instances, you'll want to subscribe to that event at the start of your program.
Maybe you are trying to catch a Corrupted State Exception. Since .NET 4 applications are unable to catch such exceptions by default. You could try to add the legacyCorruptedStateExceptionsPolicy=true entry to your configuration file as stated in the MSDN article linked above.
I have this simple code :
var g= Task.Factory.StartNew<int> (() => 8)
.ContinueWith (ant =>{throw null;})
.ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted);
try{
Console.WriteLine("1");
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{Console.WriteLine("catch"); }
The Output :
1
catch
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
msdn :
TaskContinuationOptions.NotOnFaulted
Specifies that the continuation task should not be scheduled if its
antecedent threw an unhandled exception. This option is not valid for
multi-task continuations.
ok .
And it is ok - not showing this line cause the prev line DID throw exception.
Questions :
Do I get the AggregateException exception because I haven't inspected the Exception property ?
Must I always inspect if the antecedent throw an exception ( in each line ? ) ? ( I can't check each line ! it doesn't make any sense and very annoying)
Wasn't the try catch block should have swallow the exception ? ( I thought that all exceptions bubble up to the wait method....so ? )
Do I get the AggregateException exception because I haven't inspected
the Exception property ?
No, you get an exception, because task g cancels by TPL(because, as msdn stated, this task will not scheduled if antescendent task throws an exception).
We have 3 tasks here:
Original Task (that uses StartNew)
First Continuation Task (that throws an exception)
Second Continuation Task (that prints OK) (this is g task from your code).
The issue is that you ask TPL to start 3d task only if 2nd task will finished successfully. This means that if this condition will not met TPL will cancel your newly created task entirely.
You got unobserved task exception because you have temporary task (task 2 in my list) that you never observe. An because you never observe it faulted state it will throw in finalizer to tell you about it.
You can check this by printing task's status in catch block:
catch (AggregateException ex)
{
Console.WriteLine("catch");
// Will print: Status in catch: Canceled
Console.WriteLine("Status in catch: {0}", g.Status);
}
Must I always inspect if the antecedent throw an exception ( in each
line ? ) ? ( I can't check each line ! it doesn't make any sense and
very annoying)
Yes you should observe antecedent tasks exception to avoid this issue:
static class TaskEx
{
public static Task ObserverExceptions(this Task task)
{
task.ContinueWith(t => { var ignore = t.Exception; },
TaskContinuationOptions.OnlyOnFaulted);
return task;
}
}
And then use it as following:
var g= Task.Factory.StartNew<int> (() => 8)
.ContinueWith (ant =>{throw null;})
.ObserveExceptions()
.ContinueWith (a =>{ Console.WriteLine("OK");});
try{
Console.WriteLine("1");
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{Console.WriteLine("catch"); }
UPDATE: Added solution to last bullet
Wasn't the try catch block should have swallow the exception ? ( I
thought that all exceptions bubble up to the wait method....so ? )
We have set of extension method (called TransformWith) in our project that can solve this particular issue and gain following:
Exception would bubble up to the catch block and
We'll not crash application with TaskUnobservedException
Here the usage
var g = Task.Factory.StartNew(() => 8)
.ContinueWith(ant => { throw null; })
// Using our extension method instead of simple ContinueWith
.TransformWith(t => Console.WriteLine("OK"));
try
{
Console.WriteLine("1");
// Will fail with NullReferenceException (inside AggregateExcpetion)
g.Wait();
Console.WriteLine("2");
}
catch (AggregateException ex)
{
// ex.InnerException is a NullReferenceException
Console.WriteLine(ex.InnerException);
}
And here is a extension method:
static class TaskEx
{
public static Task TransformWith(this Task future, Action<Task> continuation)
{
var tcs = new TaskCompletionSource<object>();
future
.ContinueWith(t =>
{
if (t.IsCanceled)
{
tcs.SetCanceled();
}
else if (t.IsFaulted)
{
tcs.SetException(t.Exception.InnerExceptions);
}
else
{
try
{
continuation(future);
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}
}
Do I get the AggregateException exception because I haven't inspected
the Exception property ?
Tasks always throw AggregateException : http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx
You can get the original exception using :
var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
Must I always inspect if the antecedent throw an exception ( in each
line ? ) ? ( I can't check each line ! it doesn't make any sense and
very annoying)
Yes it is anoying, you should create two continuations for each task to check exceptions : one that checks if there has been an exception to handle it, and another one to continue the operation if there was no exception see TaskContinuationOptions.OnlyOnFaulted and TaskContinuationOptions.OnlyOnRanToCompletion.
You should even create a third continuation to deal with cancellation if needed.
Wasn't the try catch block should have swallow the exception ? ( I
thought that all exceptions bubble up to the wait method....so ? )
No it won't, exceptions are not thrown at higher level, you should use TaskContinuationOptions.OnlyOnFaulted on the task continuation to check if there was an exception. You can get tasks exceptions at caller's level only with the async keyword not available in .net 4
Handle AggregateExceptions like this:
catch(AggregateException aex)
{
aex.Handle(ex =>
{
// Do some handling and logging
return true;
}
}
I'm looking at the TPL exception handling example from MSDN #
http://msdn.microsoft.com/en-us/library/dd537614(v=VS.100).aspx
The basic form of the code is:
Task task1 = Task.Factory.StartNew(() => { throw new IndexOutOfRangeException(); });
try
{
task1.Wait();
}
catch (AggregateException ae)
{
throw ae.Flatten();
}
My question is: Is this a race condition? What happens if task1 throws before the try has executed? Am I missing something that stops this being a race?
Shouldn't it be written like this instead:
try
{
Task task1 = Task.Factory.StartNew(() => { throw new IndexOutOfRangeException(); });
task1.Wait();
}
catch (AggregateException ae)
{
throw ae.Flatten();
}
No, the first example is perfectly valid.
When the exception is raised in the Task it is wrapped in an AggregateException. Only when another thread joins the task, in this example by calling task1.Wait() is the exception propogated to the joining thread. Essentially the exception is 'stored' until it can be propogated back to a thread that is waiting for the feedback.