Task class - multiple waits and exceptions - c#

usually I encapsulated a task as follows:
Task t = Task.Factory.StartNew(() =>
{
func_can_throw_exception();
}, token).
ContinueWith
((task) =>
{
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
//handle
return true;
});
}
finally
{
}
});
The questions is what happens if I wait (t.Wait();) on this task in a seperate thread (for example the GUI thread). Is this allowed. If there is an exception during task execution, how is this handled?

Don't do it like that. The better way is:
Task.Run(() => {
func_can_throw_exception();
})
.ContinueWith(task => {
do_something_with(task.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
But in the code you provided task.Wait() won't block since ContinueWith only fires after the task is finished.
In the general case, task.Wait() will block the current thread until the task is finished. If the task fails, then Wait will throw an AggregateException. But using Wait can cause deadlocks if you're not careful. It's best to use continuations in TPL code.

It's usually a bad idea to Wait on tasks since it blocks the calling thread as opposed to await which waits asynchronously. It's espically dangerous to block the GUI thread since it can very quickly lead to deadlocks.
You are handling any exception internally so unless Handle throws an exception t.Wait() would not throw any exceptions.
What you should be doing is using async-await:
try
{
await Task.Run(() => func_can_throw_exception());
}
catch (Exception e)
{
// handle exception
}

Related

Task.WhenAll().ContinueWith() throws unexpected Task Canceled exception

I am using Task.WhenAll to schedule tasks concurrently (more or less of course) and want to rethrow all exceptions rather than the first one what Task.WhenAll would do.
So I want to perfor a continuation whenever any child task faulted or has been canceled.
That's what NotOnRanToCompletion means, right?
Well this actually works but in case that all Tasks actually have completed,
I get a Task Cancelled exception on the Continuation?
var semaphore = new SemaphoreSlim(10);
var tasks = portList.Select(async port =>
{
try
{
await semaphore.WaitAsync();
await this.AddDeviceAsync(bioprocessDevicesDto, template, port);
}
finally
{
semaphore.Release();
}
});
await Task.WhenAll(tasks).ContinueWith(t =>
{
// this will make all inner exceptions available
// never null
throw t.Exception;
}, TaskContinuationOptions.NotOnRanToCompletion).ConfigureAwait(false);
That behavior is by-design and documented.
Note that your code is not awaiting the task returned from WhenAll; it's awaiting the continuation from ContinueWith.
ContinueWith is a low-level method with dangerous default behavior. Use await instead:
var tasks = portList.Select(...);
var allTask = Task.WhenAll(tasks);
try { await allTask; }
catch { throw allTask.Exception; }
When you look at the TaskContinuationOptions for NotOnRanToCompletion, it states:
This option is not valid for multi-task continuations.
I would try changing NotOnRanToCompletion to another option that handles multi-task continuations.

ContinueWith TaskContinuationOptions.OnlyOnFaulted does not seem to catch an exception thrown from a started task

I'm trying to catch an exception thrown from a task method using ContinueWith and OnlyOnFaulted like below. However I get an unhandled exception while I try to run this code.
I'd like the task to run to completion since I have handled the exception already. But Task.Wait() runs into AggregateException.
var taskAction = new Action(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
If I handle the exception in the task method like below, everything will go normal as I expect. Task Runs Into Completion, Exception gets logged and Wait succeeds also.
var taskAction = new Action(() =>
{
try
{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception ex)
{
Console.WriteLine("Catching the exception in the Action catch block only");
}
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();
My question is: Am I using the OnlyOnFaulted correctly or is it always better to handle the exception in the task method itself? I would like the main thread to continue even if task runs into exception. Also, I want to log that exception from task method.
Note: I have to wait for the task method to complete before going further (with or without errors).
To summarize(my understanding so far)
If exception from Task is handled i.e. if wait or await catches the exception then the exception would be propagated to continuedtask onfaulted.
Exception can be caught even in the task method and consumed\handled.
try
{
t.wait();
}
catch(Exception e)
{
LogError(e);
}
In above case before LogError gets called, continued task associated with the main task's onfaulted gets executed.
First of all, you aren't using OnlyOnFaulted correctly. When you use ContinueWith on a task you don't really change that task, you get back a task continuation (which in your case you disregard).
If the original task faulted (i.e. had an exception thrown inside it) it would stay faulted (so calling Wait() on it would always rethrow the exception). The continuation however would run after the task faulted and handle the exception.
That means that in your code you do handle the exception, but you're also rethrowing it with Wait(). The correct code should look like this:
Task originalTask = Task.Run(() => throw new Exception());
Task continuationTask = originalTask.ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
continuationTask.Wait()
// Both tasks completed. No exception rethrown
Now, as Yuval Itzchakov pointed out, you can handle the exception wherever you want but it would better if you were utilizing async-await to wait asynchronously if you can (you can't in Main) instead of blocking with Wait():
try
{
await originalTask;
}
catch (Exception e)
{
// handle exception
}
Am I using the TaskContinutationOptions.OnlyOnFaulted correctly or is it always better to
handle the exception in the task method itself? I would like the main
thread to continue even if task runs into exception.
You can handle exceptions either way, inside or outside. It's a matter of preference and usually depends on the use-case.
Note that one thing you aren't doing is keeping a reference to your continuation. You're using Task.Wait on the original task which propogates the exception regardless of the fact that you have a continuation which handles it.
One thing that bothers me is that you're using Task.Wait which synchronously waits instead of await which asynchronously waits. Thats the reason for the AggregationException. More so, you shouldn't block on asynchronous operations, as that will lead you down a rabit hole you probably don't want to go, with all sorts of synchronization context problems.
What i would personally do, is use await inside of ContinueWith, because its the less verbose option. Also, i'd use Task.Run over Task.Factory.StartNew:
var task = Task.Run(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
}
// Do more stuff here until you want to await the task.
try
{
await task;
}
catch (InvalidOperationException ioe)
{
// Log.
}
The original question happens to run its taskAction on a separate thread. That might not always be the case.
The answer by i3arnon solves the problem well. What if we do not want to use a separate thread? If we want to simply start a task, running it synchronously until we hit IO or a delay, and then continue about our own business. Only at the very end, we will wait for the task to complete.
How do we await the task with rethrowing its exception? Instead of wrapping it in Task.Run(), we will use an empty continuation that will always succeed when the task finishes for any reason.
// Local function that waits a moment before throwing
async Task ThrowInAMoment()
{
await Task.Delay(1000);
Console.WriteLine("Task waited for a sec");
throw new Exception("Throwing for example");
}
// Start the task without waiting for it to complete
var t = ThrowInAMoment();
// We reach this line as soon as ThrowInAMoment() can no longer proceed synchronously
// This is as soon as it hits its "await Delay(1000)"
// Handle exceptions in the task
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ t.Exception), TaskContinuationOptions.OnlyOnFaulted);
// Continue about our own business
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
// Now we want to wait for the original task to finish
// But we do not care about its exceptions, as they are already being handled
// We can use ContinueWith() to get a task that will be in the completed state regardless of HOW the original task finished (RanToCompletion, Faulted, Canceled)
await t.ContinueWith(task => {});
// Could use .Wait() instead of await if you want to wait synchronously for some reason
It looks like you are on the right track. I have ran your code and get the same results. My suggestion is to use the try/catch directly from inside the action. This will make your code clearer and allow you to log things like which thread it came from, which may be different in the continuation path.
var taskAction = new Action(() =>
{
try{
Thread.Sleep(1000);
Console.WriteLine("Task Waited for a sec");
throw (new Exception("throwing for example"));
}
catch (Exception e)
{
Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+ e);
}
});
Task t = Task.Factory.StartNew(taskAction);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
await t;
[Edit] Removed outer exception handler.

How to try..catch ThreadPool.QueueUserWorkItem code

I have bunch of Asynchronous commands. I want to write try..catch without much of repeating. Example:
_fooCommand = new AsynchronousCommand( () => { action } );
_barCommand = new AsynchronousCommand( () => { action } );
AsynchronousCommand is class that invokes Action using ThreadPool.QueueUserWorkItem( (state) => { action() } );.
Try..catch works well when is inside lambda:
_fooCommand = new AsynchronousCommand( () => { try.exception.catch } );
When outside then not:
try
_fooCommand = new AsynchronousCommand( () => {...} );
catch
Exception is not catched.
Edit
I want to catch Exception not when creating command: when executing it using command.DoExecute(this) and if possible put try..catch inside lambda.
Exceptions propagate up the call stack on the thread on which they are thrown. Because the commands run on a thread pool thread, it will be on a different thread to your try ... catch hence it doesn't get caught.
EDIT: Assuming you do actually invoke the command within the try ... catch
You can get these semantics through the use of await. It when you await something it will schedule the remainder of the method as a continuation of the previous method, meaning that the operation is performed asynchronously. However, when the awaited operation finishes, if it represents something that throws an exception, that exception will be caught and then re-thrown within the context of your next continuation, allowing you to wrap a series of asynchronous operations in a single try/catch, having the syntax and semantics you desire. A simple example might look like:
public static async Task Foo()
{
try
{
await Task.Run(() => DoSomething());
await Task.Run(() => DoSomethingElse());
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
Here DoSomething and DoSomethingElse will be run in a thread pool thread, and if either throws an exception when running, not when being started, then the catch block will be hit.

Error handling in Tasks

I have the following code
Task load = Task.Factory.StartNew(() =>
{
Threading.Thread.Sleep(5000);
throw new Exception("bad error");
});
try{
load.Wait();
}catch(AggregateException aex){
MessageBox.Show("Error Caught!");
}
Here as you can see, I create a task and throw an exception.
The exception is then caught on the UI thread. But with this this set up, the UI will be un responsive.
What is the work around for this to make the UI responsive and catch the Exception?
There is a couple of ways to handle this.
You could use ContinueWith and check there, or you could just hook into the global task exception handler TaskScheduler.UnobservedTaskException). (details to come)
ContinueWith for exception handling only:
load.ContinueWith(previousTask =>
{
//exception message here
}, TaskContinuationOptions.OnlyOnFaulted);
or for with a normal try catch:
load.ContinueWith(previousTask =>
{
try
{
previousTask.Result
}
catch(Exception ex){//message here}
});
You'll want to add a continuation to the task, rather than using Wait or any other means of blocking on the task.
load.ContinueWith(t => MessageBox.Show(t.Exception.Message)
, TaskContinuationOptions.OnlyOnFaulted);

handling exception in Tpl

I have read a lot on how to handle exceptions in TPL but don't really understand.
Lets take this example code:
var task1 = new Task(() => { throw new Exception("Throw 1"); });
var task2 = task1.ContinueWith(t => Console.WriteLine("Catch 1:{0}", t.Exception.Message),
TaskContinuationOptions.OnlyOnFaulted);
var task3 = task2.ContinueWith(t => Console.WriteLine("Continuation"));
task1.Start();
try {
task1.Wait();
}
catch (Exception ex) {
Console.WriteLine("Wait Exception: {0}", ex.Message);
}
I expected this to print
Catch 1
Continuation
But I get
Catch 1
Continuation
Wait Exception
This means that the exception is still considered unhandled when the task completes and the task finalizer will eventually tear down the application.
How do I handle the exception inside the continuation so the finalizer will not throw? At the same time I want the task to remain in the faulted state so wrapping the task in try/catch will not work.
The background is that I want to implement the async event pattern as specified here but with error handling. My complete code looks like this
public IAsyncResult Begin(AsyncCallback callback, object state, Action action) {
var task1 = new Task(action);
var task2 = task1.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
if (callback != null) {
var task3 = task2.ContinueWith(t => callback(t),
TaskScheduler.FromCurrentSynchronizationContext());
var task4 = task3.ContinueWith(t => HandleException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
}
task1.Start();
return task;
}
You do your wait on the task that fails, and if you read the documentation on Task.Wait carefully you will see that wait will rethrow the exception in this case.
But if you wait on your task3 everything should work as expected.
Of course you should keep this in mind:
When you use the OnlyOnFaulted option, it is guaranteed that the
Exception property in the antecedent is not null. You can use that
property to catch the exception and see which exception caused the
task to fault. If you do not access the Exception property, the
exception will go unhandled. Also, if you attempt to access the Result
property of a task that has been canceled or has faulted, a new
exception will be raised.
(Reference here)
And finally yet another good source on How to handle exceptions thrown by tasks
I hope this helps.

Categories