Showing posts with label Async. Show all posts
Showing posts with label Async. Show all posts

Tuesday, 23 August 2016

Why is async void bad?

Methods returning void are not awaitable. await operator can be applied only to methods which are returning Task or Task

Let's assume that method Bar is async void. Consequences of  having method Foo calling method Bar are the following:

  • This is a fire and forget model: Foo can't get information when Bar terminates as it can't await on async void method. Foo continues to run before Bar call is completed.
  • If Bar throws an exception, Foo can't catch it as there is no Task object to carry the exception object.
For these reasons async void methods shall be avoided. But this rule has a single exception; top-level events (e.g. GUI events) of void type can be async,

Links:


https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only

http://blog.stephencleary.com/2014/02/synchronous-and-asynchronous-delegate.html


http://stackoverflow.com/questions/15522900/how-to-safely-call-an-async-method-in-c-sharp-without-await

http://stackoverflow.com/questions/23285753/how-to-await-on-async-delegate

http://stackoverflow.com/questions/20624667/how-do-you-implement-an-async-action-delegate-method

Monday, 21 December 2015

OperationCanceledException vs TaskCanceledException when task is canceled


In my previous post, "How to cancel a Task", I mentioned that MSDN recommends throwing OperationCanceledException from a cancelled task. But the following example shows that canceling .NET methods makes them throw a different exception type - TaskCanceledException:

Output:


System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null


So why .NET does not follow its own rules? Why exception thrown in the example above was not OperationCanceledException?

I asked this question on SO and this is how I interpret the answer I got:

(1) System.OperationCanceledException class has been around longer than TPL. MSDN describes it as: "The exception that is thrown in a thread upon cancellation of an operation that the thread was executing." It is intended to be thrown from a thread in which operation is executed. .NET async API are actually synchronous operations (return Task, not marked as async). They don't have additional thread involved which makes OperationCanceledException not appropriate exception to be thrown.

(2) It is different to cancel a Task (setup of the engine which will execute "real work", user's operation) than to cancel operation itself. For example, we can pass already cancelled token to a task or can simply create already cancelled task. Operation and its thread are not started at all in these cases but if we await such task and catch OperationCanceledException we might think they were actually started. So it makes sense to use different type of the exception in these two different scenarios.

Example 1: Already canceled token is passed to task factory method:

Output:


System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null


Example 2: Awaiting task which is created as already canceled:

Output:


System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Program.d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null


From the points made above we can conclude that caller of the cancelable task can expect two types of exceptions:OperationCanceledException and TaskCanceledException. As the latter derives from the former, it is enough to handle OperationCanceledException if we want to handle the case of canceled task:




Further reading:
SO Q&A: OperationCanceledException VS TaskCanceledException when task is canceled
SO Q&A: Difference between OperationCanceledException and TaskCanceledException?
Andrew L Arnott: Recommended patterns for CancellationToken

Sunday, 20 December 2015

How to cancel a Task


It is a good practice to offer users a chance to cancel some long-running operation. Such operation usually has to run asynchronously and in a TPL-based design is modeled as a method returning a Task.

Cancellation model requires some flag which is accessible by the caller and from inside the operation. Caller sets the flag when wants operation to be cancelled. Operation regularly checks the flag or is subscribed to the event "flag has been set" and if flag has been set, operation will stop doing the work. This model is in .NET abstracted with two types: CancellationTokenSource class and CancellationToken structure. Caller uses CancellationTokenSource to initiate cancellation and operation uses CancellationToken to check whether it has been cancelled.

When designing an API, if provision for operation cancellation is required, we have to add a CancellationToken to a list of method's arguments. API caller invokes CancellationTokenSource.Cancel method which sets CancellationToken.IsCancellationRequested to true. Target method checks this property and can either silently return or throw OperationCanceledException. The latter approach is better as only in this case returning task will come to Canceled state.

The following code depicts the whole process of task cancellation:

CancellationToken.ThrowIfCancellationRequested() method simply checks CancellationToken.IsCancellationRequested and if it's true, it throws OperationCanceledException. The output of the code above is:


.........System.OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowIfCancellationRequested()
   at MyService.d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()
Task.IsCanceled: True
Task.IsFaulted: False
Task.Exception: null


The following code shows that the await-ed task does not get aware that the task it holds got canceled if method silently returns on cancellation:

Output:

..........Task.IsCanceled: False
Task.IsFaulted: False
Task.Exception: null


Further reading:
Andrew Arnott - Recommended patterns for CancellationToken
MSDN: Task Cancellation

Saturday, 19 December 2015

await VS Wait() when Task throws exception

await and Wait() are two versions of the operation "wait for the task to complete": one is asynchronous (non-blocking) and the other one is synchronous (blocking). They are both capable of capturing the exception thrown from the task but they behave in a different way when propagating exception information up the stack: they themselves throw different type of exception.


await (case 1) propagates the original exception so the output is:


System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()
Task.Status: Faulted
Task.IsFaulted: True
Task.Exception: System.AggregateException: One or more errors occurred. ---> System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Program.d__1.MoveNext()<---


Wait() (case 2) wraps original exception in System.AggregateException:


System.AggregateException: One or more errors occurred. ---> System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at Program.d__1.MoveNext()
---> (Inner Exception #0) System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()<---

Task.Status: Faulted
Task.IsFaulted: True
Task.Exception: System.AggregateException: One or more errors occurred. ---> System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.ArgumentNullException: Value cannot be null.
   at Program.<>c.b__1_0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()<---


Note that Task.Exception in both cases holds System.AggregateException. This difference comes from the desire of async-await implementers to keep use of await as simple as possible and to make its use to look like synchronous code as much as possible. System.AggregateException is not used in synchronous code (in non-TPL code) and we there always catch the first exception that is thrown from a try-block. That is why await is designed in such way so it throws only the first exception from a task (or aggregate of tasks) as usually we are interested only in that first exception (we usually handle the first error that occurs).

Saturday, 12 December 2015

How to set return values for methods returning Task<T>


Non-async method which returns Task<T>


If method is not marked as async and its return type is Task<T>, it has to return object of type Task<T> otherwise compiler issues an error. This is the example of the typical type mismatch error where code:


...causes a compiler error:

Error CS0029: Cannot implicitly convert type 'int' to 'System.Threading.Tasks.Task'

This can be rectified by simply returning expected type:



async method which returns Task<T>


If method is marked as async and its return type is Task<T>, it has to return object of type T, which is int in our case:


...but because async method lacks await in its body, this code generates compiler warning:

Warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

We can await only methods which return Task or Task<T> so this can be fixed by returning value from awaited completed task:


We have to be careful when calling methods returning Task<T>. If not await-ed, they return Task<T>. If awaited, they return object of Task's generic argument type, T. The following code is not awaiting method returning Task<int>:


...which causes compiler error:

Error CS4016: Since this is an async method, the return expression must be of type 'int' rather than 'Task'

Both methods are awaitable because they return object of type Task:


Asynchronous lambda

If you try to await some task in lambda expression like here:



...you will a compiler error:


Error CS4034: The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.


The fix is obvious: apply async to lambda - put it just before lambda's parameter list:


Sunday, 9 March 2014

async - await idiom

async-await idiom has been introduced in C# 5.0 and it is basically a syntactic sugar for implementation of task's continuations.

Here are some interesting excerpts from the article "Easier Asynchronous Programming with the New Visual Studio Async CTP" by Eric Lippert, which was published in MSDN Magazine in October 2011:

The work that must come after a particular task is finished is called the continuation of the task.

An await expression (...) means “evaluate this expression to obtain an object representing work that will in the future produce a result. Sign up the remainder of the current method as the callback associated
with the continuation of that task. Once the task is produced and the callback is signed up, immediately return control to my caller.”

Every time an await is encountered, the currently executing method signs up the rest of the method as the thing to do when the current task is complete, and then immediately returns. Somehow each task will complete itself—either by being scheduled to run as an event on the current thread, or because it used an I/O completion thread or worker thread—and will then cause its continuation to “pick up where it left off ” in executing the rest of the method.

Note that the method is now marked with the new async keyword; this is simply an indicator to the compiler that lets it know that in the context of this method, the keyword await is to be treated as a point where the workflow returns control to its caller and picks up again when the associated task is finished.


Asynchronous Programming with Async and Await (MSDN)
async (C# Reference)(MSDN)
await (C# Reference)(MSDN)

Asynchrony in C# 5, Part One (Eric Lippert's blog)

Whenever a task is "awaited", the remainder of the current method is signed up as a continuation of the task, and then control immediately returns to the caller. When the task completes, the continuation is invoked and the method starts up where it was before.

Asynchronous Programming in C# 5.0 part two: Whence await? (Eric Lippert's blog)
Async articles from John Skeet's blog
Async/Await FAQ
What is thread doing after returning from async point? (SO)
What happens to an `awaiting` thread in C# Async CTP?
C# Async Await Cheatsheet