Async/Await FAQ

Stephen Toub - MSFT

From time to time, I receive questions from developers which highlight either a need for more information about the new “async” and “await” keywords in C# and Visual Basic. I’ve been cataloguing these questions, and I thought I’d take this opportunity to share my answers to them.

Conceptual Overview

Where can I get a good overview of the async/await keywords?

Generally, you can find lots of resources (links to articles, videos, blogs, etc.) on the Visual Studio Async page at https://msdn.com/async. To call out just a few specific resources, the October 2011 issue of MSDN Magazine included a trio of articles that provided a good introduction to the topic. If you read them all, I recommend you read them in the following order:

  1. Asynchronous Programming: Easier Asynchronous Programming with the New Visual Studio Async CTP
  2. Asynchronous Programming: Pause and Play with Await
  3. Asynchronous Programming: Understanding the Costs of Async and Await

The .NET team blog also includes a good overview of asynchrony in .NET 4.5: Async in 4.5: Worth the Await.

Why do I need the compiler to help me with asynchronous programming?

Anders Hejlsberg’s Future directions for C# and Visual Basic talk at //BUILD/ provides a great tour through why a compiler is really beneficial here. In short, the compiler takes on the responsibility of doing the kind of complicated transformations you’d otherwise be doing by hand as you manually invert your control flow using callbacks and continuation-passing style. You get to write your code using the language’s control flow constructs, just as you would if you were writing synchronous code, and the compiler under the covers applies the transformations necessary to use callbacks in order to avoid blocking threads.

To achieve the benefits of asynchrony, can’t I just wrap my synchronous methods in calls to Task.Run?

It depends on your goals for why you want to invoke the methods asynchronously. If your goal is simply to offload the work you’re doing to another thread, so as to, for example, maintain the responsiveness of your UI thread, then sure. If your goal is to help with scalability, then no, just wrapping a synchronous call in a Task.Run won’t help. For more information, see Should I expose asynchronous wrappers for synchronous methods? And if from your UI thread you want to offload work to a worker thread, and you use Task.Run to do so, you often typically want to do some work back on the UI thread once that background work is done, and these language features make that kind of coordination easy and seamless.

The “Async” Keyword

What does the “async” keyword do when applied to a method?

When you mark a method with the “async” keyword, you’re really telling the compiler two things:

  1. You’re telling the compiler that you want to be able to use the “await” keyword inside the method (you can use the await keyword if and only if the method or lambda it’s in is marked as async). In doing so, you’re telling the compiler to compile the method using a state machine, such that the method will be able to suspend and then resume asynchronously at await points.
  2. You’re telling the compiler to “lift” the result of the method or any exceptions that may occur into the return type. For a method that returns Task or Task<TResult>, this means that any returned value or exception that goes unhandled within the method is stored into the result task. For a method that returns void, this means that any exceptions are propagated to the caller’s context via whatever “SynchronizationContext” was current at the time of the method’s initial invocation.

Does using the “async” keyword on a method force all invocations of that method to be asynchronous?

No. When you invoke a method marked as “async”, it begins running synchronously on the curren thread. So, if you have a synchronous method that returns void and all you do to change it is mark it as “async”, invocations of that method will still run synchronously. This is true regardless of whether you leave the return type as “void” or change it to “Task”. Similarly, if you have a synchronous method that returns some TResult, and all you do is mark it as “async” and change the return type to be “Task<TResult>”, invocations of that method will still run synchronously.

Marking a method as “async” does not affect whether the method runs to completion synchronously or asynchronously. Rather, it enables the method to be split into multiple pieces, some of which may run asynchronously, such that the method may complete asynchronously. The boundaries of these pieces can occur only where you explicitly code one using the “await” keyword, so if “await” isn’t used at all in a method’s code, there will only be one piece, and since that piece will start running synchronously, it (and the whole method with it) will complete synchronously.

Does the “async” keyword cause the invocation of a method to queue to the ThreadPool? To create a new thread? To launch a rocket ship to Mars?

No. No. And no. See the previous questions. The “async” keyword indicates to the compiler that “await” may be used inside of the method, such that the method may suspend at an await point and have its execution resumed asynchronously when the awaited instance completes. This is why the compiler issues a warning if there are no “awaits” inside of a method marked as “async”.

Can I mark any method as “async”?

No. Only methods that return void, Task, or Task<TResult> can be marked as async. Further, not all such methods can be marked as “async”. For example, you can’t use “async”:

  • On your application’s entry point method, e.g. Main. When you await an instance that’s not yet completed, execution returns to the caller of the method. In the case of Main, this would return out of Main, effectively ending the program.
  • On a method attributed with:
    • [MethodImpl(MethodImplOptions.Synchronized)]. For a discussion of why this is disallowed, see What’s New for Parallelism in .NET 4.5 Beta; attributing a method as Synchronized is akin to wrapping the entire body of the method with lock/SyncLock.
    • [SecurityCritical] and [SecuritySafeCritical]. When you compile an async method, the implementation / body of the method actually ends up in a compiler-generated MoveNext method, but the attributes for it remain on the signature you defined. That means that attributes like [SecuritySafeCritical] (which is meant to have a direct impact on what you’re able to do in the body of the method) would not work correctly, and thus they’re prohibited, at least for now.
  • On a method with ref or out parameters.  The caller would expect those values to be set when the synchronous invocation of the method completes, but the implementation might not set them until its asynchronous completion much later.
  • On a lambda used as an expression tree.  Async lambda expressions cannot be converted to expression trees.

Are there any conventions I should use when writing methods marked as “async”?

Yes. The Task-based Asynchronous Pattern (TAP) is entirely focused on how asynchronous methods that return Task or Task<TResult> should be exposed from libraries. This includes, but is not limited to, methods implemented using the “async” and “await” keywords. For an in-depth tour through the TAP, see the Task-based Asynchronous Pattern document.

Do I need to “Start” Tasks created by methods marked as “async”?

No.  Tasks returned from TAP methods are “hot”, meaning the tasks represent operations that are already in-progress.  Not only do you not need to call “.Start()” on such tasks, but doing so will fail if you try.  For more details, see FAQ on Task.Start.

Do I need to “Dispose” Tasks created by methods marked as “async”?

No. In general, you don’t need to Dispose of any tasks.  See Do I need to dispose of Tasks?.

How does “async” relate to the current SynchronizationContext?

For methods marked as “async” that return Task or Task<TResult>, there is no method-level interaction with the SynchronizationContext. However, for methods marked as “async” that return void, there is a potential interaction.

When an “async void” method is invoked, the prolog for the method’s invocation (as handled by the AsyncVoidMethodBuilder that is created by the compiler to represent the method’s lifetime) will capture the current SynchronizationContext (“capture” here means it accesses it and stores it). If there is a non-null SynchronizationContext, two things will be affected:

  • The beginning of the method’s invocation will result in a call to the captured context’s OperationStarted method, and the completion of the method’s execution (whether synchronous or asynchronous) will result in a call to that captured context’s OperationCompleted method. This gives the context a chance to reference count outstanding asynchronous operations; if instead the method had returned a Task or Task<TResult>, the caller could have done the same tracking via that returned task.
  • If the method completes due to an unhandled exception, the throwing of that exception will be Post’d to the captured SynchronizationContext. This gives the context a chance to deal with the failure. This is in contrast to a Task or Task<TResult>-returning async method, where the exception can be marshaled to the caller through the returned task.

If there isn’t a SynchronizationContext when the “async void” method is called, no context is captured, and then as there are no OperationStarted / OperationCompleted methods to call, none are invoked. In such a case, if an exception goes unhandled, the exception is propagated on the ThreadPool, which with default behavior will cause the process to be terminated.

The “Await” Keyword

What does the “await” keyword do?

The “await” keyword tells the compiler to insert a possible suspension/resumption point into a method marked as “async”.

Logically this means that when you write “await someObject;” the compiler will generate code that checks whether the operation represented by someObject has already completed. If it has, execution continues synchronously over the await point. If it hasn’t, the generated code will hook up a continuation delegate to the awaited object such that when the represented operation completes, that continuation delegate will be invoked. This continuation delegate will re-enter the method, picking up at this await location where the previous invocation left off. At this point, regardless of whether the awaited object had already completed by the time it was awaited, any result from the object will be extracted, or if the operation failed, any exception that occurred will be propagated.

In code, this means that when you write:

await someObject;

the compiler translates that into something like the following (this code is an approximation of what the compiler actually generates):

private class FooAsyncStateMachine : IAsyncStateMachine
{
    // Member fields for preserving “locals” and other necessary state
    int $state;
    TaskAwaiter $awaiter;
    …
    public void MoveNext()
    {
        // Jump table to get back to the right statement upon resumption
        switch (this.$state)
        {
            …
            case 2: goto Label2;
            …
        }
        …
        // Expansion of “await someObject;”
        this.$awaiter = someObject.GetAwaiter();
        if (!this.$awaiter.IsCompleted)
        {
            this.$state = 2;
            this.$awaiter.OnCompleted(MoveNext);
            return;
            Label2:
        }
        this.$awaiter.GetResult();
        …
    }
}

What are awaitables? What are awaiters?

While Task and Task<TResult> are two types very commonly awaited, they’re not the only ones that may be awaited.

An “awaitable” is any type that exposes a GetAwaiter method which returns a valid “awaiter”. This GetAwaiter method may be an instance method (as it is in the case of Task and Task<TResult>), or it may be an extension method.

An “awaiter” is any type returned from an awaitable’s GetAwaiter method and that conforms to a particular pattern. The awaiter must implement the System.Runtime.CompilerServices.INotifyCompletion interface, and optionally may implement the System.Runtime.CompilerServices.ICriticalNotifyCompletion interface. In addition to providing an implementation of the OnCompleted method that comes from INotifyCompletion (and optionally the UnsafeOnCompleted method that comes from ICriticalNotifyCompletion), an awaiter must also provide an IsCompleted Boolean property, as well as a parameterless GetResult method. GetResult returns void if the awaitable represents a void-returning operation, or it returns a TResult if the awaitable represents a TResult-returning operation.

Any type that follows the awaitable pattern may be awaited. For a discussion of several approaches to implementing custom awaitables, see await anything;. You can also implement awaitables customized for very specific situations: for some examples, see Advanced APM Consumption in Async Methods and Awaiting Socket Operations.

Where can’t I use “await”?

You can’t use await:

Is “await task;” the same thing as “task.Wait()”?

No.

“task.Wait()” is a synchronous, potentially blocking call: it will not return to the caller of Wait() until the task has entered a final state, meaning that it’s completed in the RanToCompletion, Faulted, or Canceled state. In contrast, “await task;” tells the compiler to insert a potential suspension/resumption point into a method marked as “async”, such that if the task has not yet completed when it’s awaited, the async method should return to its caller, and its execution should resume when and only when the awaited task completes. Using “task.Wait()” when “await task;” would have been more appropriate can lead to unresponsive applications and deadlocks; see Await, and UI, and deadlocks! Oh my!.

There are some other potential pitfalls to be aware of when using “async” and “await”. For some examples, see:

Is there a functional difference between “task.Result” and “task.GetAwaiter().GetResult()”?

Yes, but only if the task completes non-successfully.  If the task ends in the RanToCompletion state, these are completely equivalent statements.  If, however, the task ends in the Faulted or Canceled state, the former will propagate the one or more exceptions wrapped in AggregateException, while the latter will propagate the exception directly (and if there are more than one in the task, it’ll just propagate one of them).  For background on why this difference exists, see Task Exception Handling in .NET 4.5.

How does “await” relate to the current SynchronizationContext?

This is entirely up to the type being awaited. For a given await, the compiler generates code that ends up calling an awaiter’s OnCompleted method, passing in the continuation delegate to be executed. The compiler-generated code knows nothing about SynchronizationContext, and simply relies on the awaited object’s OnCompleted method to invoke the provided callback when the awaited operation completes. It’s the OnCompleted method, then, that’s responsible for making sure that the delegate is invoked in the “right place,” where “right place” is left entirely up to the awaiter.

The default behavior for awaiting a task (as implemented by the TaskAwaiter and TaskAwaiter<TResult> types returned from Task’s and Task<TResult>’s GetAwaiter methods, respectively) is to capture the current SynchronizationContext before suspending, and then when the awaited task completes, if there had been a current SynchronizationContext that got captured, to Post the invocation of the continuation delegate back to that SynchronizationContext. So, for example, if you use “await task;” on the UI thread of your application, OnCompleted when invoked will see a non-null current SynchronizationContext, and when the task completes, it’ll use that UI’s SynchronizationContext to marshal the invocation of the continuation delegate back to the UI thread.

If there isn’t a current SynchronizationContext when you await a Task, then the system will check to see if there’s a current TaskScheduler, and if there is, the continuation will be scheduled to that when the task completes.

If there isn’t such a context or scheduler to force the continuation back to, or if you do “await task.ConfigureAwait(false)” instead of just “await task;”, then the continuation won’t be forced back to the original context and will be allowed to run wherever the system deems appropriate. This typically means either running the continuation synchronously wherever the awaited task completes or running the continuation on the ThreadPool.

Can I use “await” in console apps?

Sure. You can’t use “await” inside of your Main method, however, as entry points can’t be marked as async. Instead, you can use “await” in other methods in your console app, and then if you call those methods from Main, you can synchronously wait (rather than asynchronously wait) for them to complete, e.g.

public static void Main()
{
    FooAsync().Wait();
}

private static async Task FooAsync()
{
    await Task.Delay(1000);
    Console.WriteLine(“Done with first delay”);
    await Task.Delay(1000);
}

You could also use a custom SynchronizationContext or TaskScheduler to achieve similar capabilities. For more information, see:

Can I use “await” with other asynchronous patterns, like the Asynchronous Programming Model (APM) pattern and the Event-based Async Pattern (EAP)?

Sure. You can either implement a custom awaitable for your asynchronous operation, or you can convert the existing asynchronous operation to something that’s already awaitable, like Task or Task<TResult>. Here are some examples:

Does the code generated by async/await result in efficient asynchronous execution?

For the most part, yes, as a lot of work has been done to optimize the code generated by the compiler and the .NET Framework methods on which the generated code relies. For more information, including on best practices for minimizing the overhead of using tasks and async/await, see:

12 comments

Discussion is closed. Login to edit/delete existing comments.

  • Rahul Bhansali 0

    Hey @Stephen. Thanks for this post and compiling these FAQs. I have a question that I’m researching for quite sometime and haven’t found anything that helps me understand the behavior clearly; it related to the use of .ConfigureAwaite(false) in web api applications.
    What I have understood so far is ConfigureAwait(continueOnCapturedContext: false) serves two purposes – 1) It’s offers protection against a caller who makes the (unfortunate) decision to make a blocking call on an async method. 2) If there isn’t a need to continue on the same context then why bother; I’m speculating that it might have a slight positive improvement on performance if it didn’t need to do part i.e. continuing on captured context. 
    What I haven’t understood is the relationship between HttpContext and SynchronizationContext. Last week a colleague of mine who had made the wise decision of removing blocking calls on async methods and use ConfigureAwait(false) was confronted by the QA when a build broke. Apparently at some later point, the code relies on trying to get the user id from the HttpContext and that was returning null. They squarely put the blame on his change involving ConfigureAwait(false). My colleague and I were unable to defend our use of ConfigureAwait(false) because we don’t quite understand the relationship between HttpContext and SynchronizationContext. Consequently, we had to undo our changes. 
    Later when I went and performed some test, I had no trouble getting the user from the HttpContext. So I’m not sure if it’s a intermittent issue because we used ConfigureAwait(false) or was it a completely different issue that caused the user to be null when fetching from HttpContext after the async call. 
    My question to you: If we have code that is relying on HttpContext to get details about the currnet user, how does resuming/continuing on a different thread that DID NOT capture the current SynchronizationContext (i.e. using ConfigureAwait(false)) affect our ability to get user info from the HttpContext? 
    Thank you for taking the time to demystify this for me. 

    • Stephen Toub 0

      > What I haven’t understood is the relationship between HttpContext and SynchronizationContext
      In ASP.NET on .NET Framework, there’s a SynchronizationContext that’s used to represent the current request, ensuring that only one operation at a time associated with that request runs, and making the HttpContext.Current available to that code, regardless of what thread it’s on.
      > If we have code that is relying on HttpContext to get details about the currnet user, how does resuming/continuing on a different thread that DID NOT capture the current SynchronizationContext (i.e. using ConfigureAwait(false)) affect our ability to get user info from the HttpContext?
      When you await a Task, and that Task hasn’t yet completed, by default the await will look to see whether there’s a current SynchronizationContext, and if it finds one, stores it away, and then when the Task eventually completes, the continuation is queued to the SynchronizationContext.  ConfigureAwait(false) just tweaks that so that the await doesn’t look for one and just pretends there wasn’t one.  But if the task was already completed by the time it was awaited, then the code is just going to continue executing synchronously, and the presence of ConfigureAwait doesn’t matter or affect anything.  In other words, ConfigureAwait(false) doesn’t guarantee that the code after it is not running in the original SynchronizationContext… it just says it’s not required.

  • jason duff 0

    Hello Stephen,
    I read your  FAQ article about async/await and it’s a great read. I have a question to ask though and I couldn’t found an answer to it on multiple blogs.
    When we use await keyword with let’s say an API call, does that operation happens on Main thread too ?
    a) If yes, how come then the Main thread (UI thread for GUI) continue with remainder of the method ?
    b) If No, then it violates the No thread spwan theory of async and await.
    Could you please clarify ?

    • Stephen Toub 0

      > When we use await keyword with let’s say an API call, does that operation happens on Main thread too ? a) If yes, how come then the Main thread (UI thread for GUI) continue with remainder of the method ? b) If No, then it violates the No thread spwan theory of async and await. Could you please clarify ?
      await on a Task hooks up a continuation/callback to that Task: when the Task completes, it invokes or queues that callback.  If the await was done when there was a current SynchronizationContext, then by default the callback is passed to that SynchronizationContext’s Post method for it to do with as it wants.  If you’re in a UI app, such as in Windows Forms, Windows Forms will have published a WindowsFormsSynchronizationContext on its UI thread.  When you’re on that thread and you await something then, the await will see the current context, store it along with the callback in the Task, and then when the Task completes, it’ll pass the callback to the WindowsFormSynchronizationContext.Post method.  WFSC.Post takes that callback and marshals its invocation back to the UI thread, e.g. via Control.BeginInvoke.

  • uknown uknown 0

    Hello Stephen, could you answer of my questions. The TaskAwaiter has implementations of two interfaces: INotifyCompletion and ICriticalNotifyCompletion. Why compiler always choose UnsafeOnCompleted method for register continuation of task? And, if UnsafeOnCompleted should suppress flow to ExecutionContext, why this method doesn’t do it? The continuation will be performed in the execution context of the calling thread, despite the instruction to suppress it. Why?

    • Stephen Toub - MSFTMicrosoft employee 0

      > Why compiler always choose UnsafeOnCompleted method for register continuation of task?

      It prefers ICriticalNotifyCompletion because it’s more efficient.

      > if UnsafeOnCompleted should suppress flow to ExecutionContext, why this method doesn’t do it? The continuation will be performed in the execution context of the calling thread, despite the instruction to suppress it. Why?

      ExecutionContext always flows across awaits; that’s handled by the async method builder.  Thus, having the awaiter do it as well would be unnecessary duplication, and so UnsafeOnCompleted is preferred.  The awaiter APIs can be called by anyone, and they were introduced at a time when we still believed in the code-access security model, and so we wanted OnCompleted to always be available (which would flow ExecutionContext if called directly) and then there was the SecurityCritical UnsafeOnCompleted that the compiler could use in async methods.

      • uknown uknown 0

        Thank for answers, Stephen. Could you ask a few more questions, please?1) This means that better to implement interface ICriticalNotifyCompletion for your type of awaiter? Coz, it is more productive and we don’t capture executionContext twice?2) So, it doesn’t matter which method will be called (AwaitOnCompleted or AwaitUnsafeOnCompleted)? ExecutionContext of calling thread will be captured at 100% of cases by AsyncCoreBuilder? And continuation of MoveNext method will be running at 100% cases in captured executionContext, despite the apparent suppression?Thank

        • Stephen Toub - MSFTMicrosoft employee 0

          > This means that better to implement interface ICriticalNotifyCompletion for your type of awaiter?

          Yes.

          > So, it doesn’t matter which method will be called (AwaitOnCompleted or AwaitUnsafeOnCompleted)?

          If the awaiter is correctly implemented, they should be functionally identical when used indirectly via the infrastructure of an async method.

          > ExecutionContext of calling thread will be captured at 100% of cases by AsyncCoreBuilder?

          The async method builder will always access ExecutionContext.Capture() to get the current context.  But it’s possible a developer could have called ExecutionContext.SuppressFlow(), in which case that Capture() call will return null and the method builder won’t flow context (but then neither would AwaitOnCompleted).

          • uknown uknown 0

            Thank you very much for the information. 

          • uknown uknown 0

            Stephen, i would like to clarify. ExecutionContext is captured in method GetCompletion of AsyncMethodBuilderCore? Where does GetCompletion method internally use method FastCapture on the ExecutionContext type?

  • vinayk vamos 0

    Hello Stephen,
    Is there any way we can call await method inside a constructor without using Task.Run.

    Thanks in advance.

    • Stephen Toub - MSFTMicrosoft employee 0

      await can’t be used in a ctor. You can of course use await in a separate async method that you call from the ctor. If that async method returns a Task, you can then choose how to handle that, such as storing it into a field that subsequent methods on the instance await. Or you could choose to not have a public ctor and just have a static factory method, that can be async.

Feedback usabilla icon