I'm trying to understand some (in my eyes) weird behaviour. I have a call to some async method and want to retrieve its result. (DeleteIndexAsync return a Task<bool>)
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem))
.ContinueWith(t =>
{
//handle and log exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
In this scenario Result is false and the Status is WaitingForActivation.
Whereas this code does what I want
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem));
deleteTask.ContinueWith(t =>
{
//handle and log exceptions
return false;
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
Can someone explain why? And is it possible to use async / await instead of Task here?
Edit:
var deleteTask = Task.Run(() => ThrowEx());
bool errorOccurred = false;
deleteTask.ContinueWith(t =>
{
errorOccurred = true;
}, TaskContinuationOptions.OnlyOnFaulted);
if (errorOccurred)
{
return true;
}
If you chain the calls, like in the first example, the value you are assigning to the deleteTask variable is actually the second Task. This is the one that is supposed to run only on failure of the first task (the one calling DeleteIndexAsync).
This is because both Task.Run and Task.ContinueWith return Tasks that they create. It explains why in the first example you get Status == WaitingForActivation. In the first snippet, accessing deleteTask.Result would cause an exception to be thrown. In case DeleteIndexAsync threw, it would be an AggregateException containing original exception (unless you accessed t.Exception), otherwise it would be stating that the "Operation was cancelled" - this is because you try to get the result of the task that was scheduled conditionally and the condition was not met.
If you made method containing the snipped async you could do it like this (not tested):
bool success = false;
try
{
success = await DeleteIndexAsync(localItem);
}
catch (Exception) {}
if (!success)
{
//TODO: handler
}
Regarding question edit:
Using captured variable should help, but your current solution introduces race condition. In this case it would be better to do like this:
var deleteTask = Task.Run(() => ThrowEx());
try
{
deleteTask.Wait();
}
catch (Exception ex)
{
return true;
}
but at this point you can drop the asynchronous call entirely, because you wait for the result immediately - unless this example simplifies work that can be done between Run and Wait)
I have a call to some async method and want to retrieve its result.
The best way to do this is with await, not Result.
Can someone explain why?
In the first example, deleteTask is the task returned from Task.Run. In the second example, deleteTask is the task returned from ContinueWith. This task never actually executes unless DeleteIndexAsync throws an exception.
And is it possible to use async / await instead of Task here?
Yes, that's the best approach. When working with asynchronous code, you should always use await instead of ContinueWith. In this case, the TaskContinuationOptions.OnlyOnFaulted means you should put the continuation code in a catch block after the await:
bool deleteResult;
try
{
deleteResult = await Task.Run(() => DeleteIndexAsync(localItem));
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
Or, since it looks like DeleteIndexAsync is asynchronous, removing Task.Run would be more appropriate:
bool deleteResult;
try
{
deleteResult = await DeleteIndexAsync(localItem);
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
Related
I have to execute about 10 tasks and then obtain their results (all different). I have created a wrapper to catch the exceptions, I don't want to know which one failed, but if a failure it should not continue. I also use WhenAll for this, but the problem is that then I get the results of each one and I get an unhandled exception, is there no way not to continue if there are any exceptions from my wrapper?
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
var result = await 1Task; <-------------exception
TaskService:
public async Task RunSafeAsync(Func<Task> task)
{
try
{
await task();
}
catch (Exception ex)
{
}
}
You can't retrieve the result from a function that has faulted. It doesn't have a result-- because it never finished. Attempting to retrieve its result (as you are) is going to throw an exception, because there is nothing to assign to the variable that is supposed to accept the return value.
To avoid this, check if the task is faulted before attempting to retrieve the result.
var 1Task = api.1TaskAsync();
var 2Task = api.2TaskAsync();
var 3Task = api.3TaskAsync();
await taskService.RunSafeAsync(() => Task.WhenAll(1Task, 2Task, 3Task));
if (!1Task.IsFaulted)
{
var result = await 1Task;
If you just want to not continue if there was an exception in any of the methods then don't wrap WhenAll and react if it throws.
try
{
await Task.WhenAll(...);
// None of the tasks threw an exception;
}
catch (Exception ex)
{
// One or more tasks threw an exception
return / throw / etc.
}
//Continue the happy path
Cancellation token
If you need to attempt tasks simultaneously and abort all tasks if one fails than you'll need to look at the cancellation token and activate the token if any exceptions occur (not in the catch for whole WhenAll, but each tasks would have to be executed in a try..catch).
Maybe sequentially?
BTW. With .WhenAll all tasks will be attempted. Maybe you actually want to execute the tasks in sequence and continue only if there are not exceptions:
try
{
await F1();
await F2();
}
catch(Exception ex)
{
}
This doesn't seem slick but it makes it very clear that task 2 will not run unless task 1 is a success.
In my client application that uses webApi I have a plethora of methods that make calls to webApi asynchronously like this:
var task = Task.Run(async () => await this.SaveObject(User));
return task.Result.Content;
The async function can throw an exception if something is wrong with object being saved. In this case my exception will be handled on the client based on it's type. Problem is when async task throws an exception, task.Result.Content buries in within System.AggregateException.
Right now I handle it like this:
try
{
var task = Task.Run(async () => await this.saveObject(User)); return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
I have too many methods that do it the same way. I was wondering if there is a way to avoid using try/catch block in each and every method. Maybe there is an exception filter mechanism, sorta like used on webApi to catch all exceptions within the class in one place? Maybe some attribute I can mark needed methods with?
First off I would recommend that you don't use .Result on a Task. See https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html or https://montemagno.com/c-sharp-developers-stop-calling-dot-result/.
If you follow the above advice and await a Task within a try block, it will throw the actual exception rather than an AggregateException so you might be able to avoid your rethrow code altogether.
Otherwise, if you really want to stick with your .Result code, you can write a generic wrapper method that does common error handling for you:
try
{
var task = Task.Run(async () => await this.Object(User));
return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
To something like:
return RunAsync(() => this.Object(User));
private T RunAsync<T>(Func<Task<T>> func)
{
try
{
var task = Task.Run(func);
return task.Result;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
}
EDIT:
I've just realised there's another way (see http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html) which is slightly more "hacky" since it feels more hidden but this:
var task = Task.Run(async () => await this.Object(User));
return task.GetAwaiter().GetResult().Content;
The .GetAwaiter().GetResult() will synchronously wait for the Task (as per .Result) but will not wrap any thrown exceptions in an AggregateException - which seems to be your desire.
When I run this code everything works fine:
public async void InvokePlugin(MyObject xTask)
{
try
{
var hndlr = new TimeoutHandler(RunTask);
var asyncResult = hndlr.BeginInvoke(xTask, null, new object());
if (!asyncResult.AsyncWaitHandle.WaitOne(xTask.Timeout, false))
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
hndlr.EndInvoke(asyncResult);
}
catch (Exception ex)
{
//Handle Exceptions
}
}
private delegate void TimeoutHandler(MyObject xTask);
I want to update this code to use Async/Await. I tried doing it like this:
public async void InvokePlugin(MyObject xTask)
{
try
{
var runTask = Task.Run(() => { RunTask(xTask); });
if (await Task.WhenAny(runTask, Task.Delay(xTask.Timeout)) == runTask)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await runTask;
}
else
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
}
catch (Exception ex)
{
//Handle Exceptions
}
}
...but it's not working. Clearly I'm doing somethign wring. It does call the RunTask Method and executes the first 2 lines fine but then it just ends and I can't seem to catch the exception in either the TaskRun method or code above. All I see in the Output windows is "Program has exited with code 0 (0x0)."
If the experts out there can either point me to what I'm doing wrong or give me suggestions as to how I can catch the exception and handle it I would be very grateful.
Also if you feel I missed any important details please ask and I will update my question.
Usually I'd say if it works don't fix it but in this case I'm trying to rearchitect a bit to allow for some enhancements so here I am.
Change async void to async Task. See my article on async best practices for more information.
After you do this, consume it asynchronously:
await the task, allowing async to grow.
async and await will naturally grow upward through your code base, until they reach Main, which cannot be async.
In your Main method, call GetAwaiter().GetResult() on the "top" task.
Blocking on asynchronous code is generally not a good idea, but blocking on a single task in a Console app's Main method is an exception to that rule.
I have a class Worker which is doing some work (with simulated workload):
public class Worker
{ ...
public void DoWork(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(2000);
}
}
Now I want to use this method in a Task.Run (from my Windows Forms App,at button-click) which can be cancelled:
private CancellationTokenSource _ctSource;
try
{
Task.Run(() =>
{
_worker.DoWork(_ctSource.Token);
},_ctSource.Token);
}
catch (AggregateException aex)
{
String g = aex.Message;
}
catch (OperationCanceledException ex)
{
String g = ex.Message;
}
catch (Exception ex)
{
String g = ex.Message;
}
But when the task is started, I can't cancel it with _ctSource.Cancel();
I get an error in visual studio that the OperationCanceledException is not handled!
But I surrounded the Task.Run Call in a try-catch-clause! The Exception which ocurrs in the Worker object should thrown up or not?
What is the problem?
Your Task.Run call creates the task and then returns immediately. It doesn't ever throw. But the task it creates may fail or be canceled later on.
You have several solutions here:
Use await:
await Task.Run(...)
Attach a continuation depending on the failure/cancellation case:
var task = Task.Run(...);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
Attach a single continuation on failure:
Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
The solution you can/should use depends on the surrounding code.
You need to new the token
private CancellationTokenSource _ctSource = new CancellationTokenSource();
Why are throwing an expectation in DoWork?
Exception from one thread don't bubble up another thread that started the thread.
Cancellation in Managed Threads
If a parallel Task throws an exception it'll return execution and will have it's Exception property (as an AggregateException, you should check for its InnerException) set (and either its IsCanceled or IsFaulted property set to true). Some minimal sample code from a project of mine which escalates the exception to the main thread:
var t = new Task(Initialize);
t.Start();
while (!t.IsCompleted && !t.IsFaulted)
{
// Do other work in the main thread
}
if (t.IsFaulted)
{
if (t.Exception != null)
{
if(t.Exception.InnerException != null)
throw t.Exception.InnerException;
}
throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
}
If you use a CancellationTokenSource it should be easy to enhance this to check for IsCanceled (instead of IsFaulted)
You can also use Task.Wait() instead of the while loop... in my project and in that precise case it seemed more appropiate to use the while loop, but you need to wait for the Task to end in one way or another.
If you use Task.Run() you can use a .ContinueWith(Task) which will have the original task passed in (where you can check for IsFaulted or IsCanceled), or have it run only on faulted execution, at your will.
Say that I have code like this:
serviceCallFinished = false;
Task.Factory.StartNew(() => {
response = ServiceManager.GeneralService.ServiceMethod(loginName, password);
}).ContinueWith(parentTask => { serviceCallFinished = true; });
while (!serviceCallFinished)
Thread.Sleep(100);
if (response.UserValid) { }
In this case the ServiceMethod will throw a Exception but this is never shown, instead I will get a null reference on the response.UserValid?
Why is the exception not handed over to my UI thread?
Note : This is on the UI thread and my idea is to not block the thread during the servicecall.
You don't need to use a flag like that just to wait for an async task to finish. Use Wait()
Task t = Task.Factory.StartNew(() => {
response = ServiceManager.GeneralService.ServiceMethod(loginName, password);
});
try
{
t.Wait();
}
catch (AggregateException e)
{
...
}
the Wait() call will elevate any exceptions within an overall AggregateException.
From the comments and contrary to the example posted, it seems like you want logic that returns to the caller while a response is being awaited, and then do a bunch of stuff afterwards. It also sounds like the stuff afterwards contains a lot of very complex methods that you want to write procedurally. The C# team has heard your problem and many like it and developed the async/await framework that will do exactly that, slated for C# 5. Alternatively, if it is an option for you, there is an AsyncCTP available here: http://www.microsoft.com/download/en/details.aspx?id=9983 HTH.
Look at How to: Handle Exceptions Thrown by Tasks
There is also a good sample.
Wha you could do is basically replace your sleeping loop with:
try
{
task.Wait();
}
catch (AggregateException ae)
{
...
}
Why don't you write code just like this:
Task.Factory.StartNew(() =>
{
response = ServiceManager.GeneralService.ServiceMethod(
loginName, password);
})
.ContinueWith(parentTask => { if (response.UserValid) { } });
It seems more clear and more responsive.