{"id":1065,"date":"2017-11-30T02:09:20","date_gmt":"2017-11-29T18:09:20","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/seteplia\/?p=1065"},"modified":"2019-06-11T00:11:56","modified_gmt":"2019-06-11T07:11:56","slug":"dissecting-the-async-methods-in-c","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/dissecting-the-async-methods-in-c\/","title":{"rendered":"Dissecting the async methods in C#"},"content":{"rendered":"<p><strong>The async series<\/strong><\/p>\n<ul>\n<li><strong>Dissecting the async methods in C#<\/strong>.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/extending-the-async-methods-in-c\/\">Extending the async methods in C#<\/a>.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/the-performance-characteristics-of-async-methods\/\">The performance characteristics of the async methods in C#<\/a>.<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/\/one-user-scenario-to-rule-them-all\/\">One user scenario to rule them all<\/a>.<\/li>\n<\/ul>\n<p>The C# language is great for developer&#8217;s productivity and I&#8217;m glad for the recent push towards making it more suitable for high-performance applications.<\/p>\n<p>Here is an example: C# 5 introduced &#8216;async&#8217; methods. The feature is very useful from a user&#8217;s point of view because it helps combining several task-based operations into one. But this abstraction comes at a cost. Tasks are reference types causing heap allocations everywhere they&#8217;re created, even in cases where the &#8216;async&#8217; method completes synchronously. With C# 7, async methods can return task-like types such as <code>ValueTask<\/code> to reduce the number of heap allocations or avoid them altogether in some scenarios.<\/p>\n<p>In order to understand how all of this is possible, we need to look under the hood and see how async methods are implemented.<\/p>\n<p>But first, a little bit of history.<\/p>\n<p>Classes <code>Task<\/code> and <code>Task&lt;T&gt;<\/code> were introduced in .NET 4.0 and, from my perspective, made a huge mental shift in area of asynchronous and parallel programming in .NET. Unlike older asynchronous patterns such as the <code>BeginXXX<\/code>\/<code>EndXXX<\/code> pattern from .NET 1.0 (also known as <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/asynchronous-programming-patterns\/asynchronous-programming-model-apm\">&#8220;Asynchronous Programming Model&#8221;<\/a>) or <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/standard\/asynchronous-programming-patterns\/event-based-asynchronous-pattern-overview\">Event-based Asynchronous Pattern<\/a> like <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/winforms\/controls\/backgroundworker-component\"><code>BackgroundWorker<\/code><\/a> class from .NET 2.0, tasks are composable.<\/p>\n<p>A task represents a unit of work with a promise to give you results back in the future. That promise could be backed by IO-operation or represent a computation-intensive operation. It doesn&#8217;t matter. What does matter is that the result of the operation is self-sufficient and is a first-class citizen. You can pass a &#8220;future&#8221; around: you can store it in a variable, return it from a method, or pass it to another method. You can &#8220;join&#8221; two &#8220;futures&#8221; together to form another one, you can wait for results synchronously or you can &#8220;await&#8221; the result by adding &#8220;continuation&#8221; to the &#8220;future&#8221;. You can decide what to do if the operation succeeded, faulted or was canceled, just using a task instance alone.<\/p>\n<p>Task Parallel Library (TPL) had changed the way we think about concurrency and C# 5 language made a step forward by introducing <code>async<\/code>\/<code>await<\/code>. Async\/await helps to compose tasks and gives the user an ability to use well-known constructs like <code>try\/catch<\/code>, <code>using<\/code> etc. But like any other abstraction <code>async\/await<\/code> feature has its cost. And to understand what the cost is, you have to look under the hood.<\/p>\n<h4>Async method internals<\/h4>\n<p>A regular method has just one entry point and one exit point (it could have more than one <code>return<\/code> statement but at the runtime there is just one exist point for a given call). But async methods (*) and iterators (methods with <code>yield return<\/code>) are different. In the case of an async method, a method caller can get the result (i.e. <code>Task<\/code> or <code>Task&lt;T&gt;<\/code>) almost immediately and then &#8220;await&#8221; the actual result of the method via the resulting task.<\/p>\n<p>(*) Let&#8217;s define the term &#8220;async method&#8221; as a method marked with contextual keyword <code>async<\/code>. It doesn&#8217;t necessarily mean that the method executes asynchronously. It doesn&#8217;t mean that the method is asynchronous at all. It only means that the compiler performs some special transformation to the method.<\/p>\n<p>Let&#8217;s consider the following async method:<\/p>\n<pre class=\"lang:default decode:true \">class StockPrices\r\n{\r\n    private Dictionary&lt;string, decimal&gt; _stockPrices;\r\n    public async Task&lt;decimal&gt; GetStockPriceForAsync(string companyId)\r\n    {\r\n        await InitializeMapIfNeededAsync();\r\n        _stockPrices.TryGetValue(companyId, out var result);\r\n        return result;\r\n    }\r\n \r\n    private async Task InitializeMapIfNeededAsync()\r\n    {\r\n        if (_stockPrices != null)\r\n            return;\r\n \r\n        await Task.Delay(42);\r\n        \/\/ Getting the stock prices from the external source and cache in memory.\r\n        _stockPrices = new Dictionary&lt;string, decimal&gt; { { \"MSFT\", 42 } };\r\n    }\r\n}<\/pre>\n<p>Method <code>GetStockPriceForAsync<\/code> ensures that the <code>_stockPrices<\/code> map is initialized and then gets the value from the cache.<\/p>\n<p>To better understand what the compiler does or can do, let&#8217;s try to write a transformation by hand.<\/p>\n<h4>Deconstructing an async method by hand<\/h4>\n<p>The TPL provides two main building blocks that help us construct and join tasks: task continuation using <code>Task.ContinueWith<\/code> and the <code>TaskCompletionSource&lt;T&gt;<\/code> class for constructing tasks by hand.<\/p>\n<pre class=\"lang:default decode:true \">class GetStockPriceForAsync_StateMachine\r\n{\r\n    enum State { Start, Step1, }\r\n    private readonly StockPrices @this;\r\n    private readonly string _companyId;\r\n    private readonly TaskCompletionSource&lt;decimal&gt; _tcs;\r\n    private Task _initializeMapIfNeededTask;\r\n    private State _state = State.Start;\r\n \r\n    public GetStockPriceForAsync_StateMachine(StockPrices @this, string companyId)\r\n    {\r\n        this.@this = @this;\r\n        _companyId = companyId;\r\n    }\r\n \r\n    public void Start()\r\n    {\r\n        try\r\n        {\r\n            if (_state == State.Start)\r\n            {\r\n                \/\/ The code from the start of the method to the first 'await'.\r\n \r\n                if (string.IsNullOrEmpty(_companyId))\r\n                    throw new ArgumentNullException();\r\n \r\n                _initializeMapIfNeededTask = @this.InitializeMapIfNeeded();\r\n \r\n                \/\/ Update state and schedule continuation\r\n                _state = State.Step1;\r\n                _initializeMapIfNeededTask.ContinueWith(_ =&gt; Start());\r\n            }\r\n            else if (_state == State.Step1)\r\n            {\r\n                \/\/ Need to check the error and the cancel case first\r\n                if (_initializeMapIfNeededTask.Status == TaskStatus.Canceled)\r\n                    _tcs.SetCanceled();\r\n                else if (_initializeMapIfNeededTask.Status == TaskStatus.Faulted)\r\n                    _tcs.SetException(_initializeMapIfNeededTask.Exception.InnerException);\r\n                else\r\n                {\r\n                    \/\/ The code between first await and the rest of the method\r\n \r\n                    @this._store.TryGetValue(_companyId, out var result);\r\n                    _tcs.SetResult(result);\r\n                }\r\n            }\r\n        }\r\n        catch (Exception e)\r\n        {\r\n            _tcs.SetException(e);\r\n        }\r\n    }\r\n \r\n    public Task&lt;decimal&gt; Task =&gt; _tcs.Task;\r\n}\r\n \r\npublic Task&lt;decimal&gt; GetStockPriceForAsync(string companyId)\r\n{\r\n    var stateMachine = new GetStockPriceForAsync_StateMachine(this, companyId);\r\n    stateMachine.Start();\r\n    return stateMachine.Task;\r\n}<\/pre>\n<p>The code is verbose but relatively straightforward. All the logic from <code>GetStockPriceForAsync<\/code> is moved to <code>GetStockPriceForAsync_StateMachine.Start<\/code> method that uses <a href=\"https:\/\/en.wikipedia.org\/wiki\/Continuation-passing_style\">&#8220;continuation passing style&#8221;<\/a>. The general algorithm of our async transformation is to split the original method is split into pieces at <code>await<\/code> boundaries. The first block is the code from the start of the method to the first <code>await<\/code>. The second block &#8211; from the first <code>await<\/code> to the second <code>await<\/code>. The third block &#8211; from code above to the third one or until the end of the method, and so forth:<\/p>\n<pre class=\"lang:default decode:true \">\/\/ Step 1 of the generated state machine:\r\n \r\nif (string.IsNullOrEmpty(_companyId)) throw new ArgumentNullException();\r\n_initializeMapIfNeededTask = @this.InitializeMapIfNeeded();<\/pre>\n<p>Every awaited task now becomes a field of the state machine, and the <code>Start<\/code> method subscribes itself as a continuation of each of these tasks:<\/p>\n<pre class=\"lang:default decode:true \">_state = State.Step1;\r\n_initializeMapIfNeededTask.ContinueWith(_ =&gt; Start());<\/pre>\n<p>Then, when the task finishes, the <code>Start<\/code> method is called back and the <code>_state <\/code>field is checked to understand what stage we&#8217;re in. The logic then checks whether the task was finished successfully, was canceled, or successful. In the latter case, the state machine moves forward and runs the next block of code. When everything is done, the state machine sets the result of the <code>TaskCompletionSource&lt;T&gt;<\/code> instance and the resulting task returned from <code>GetStockPricesForAsync<\/code> changes its state to completed.<\/p>\n<pre class=\"lang:default decode:true \">\/\/ The code between first await and the rest of the method\r\n \r\n@this._stockPrices.TryGetValue(_companyId, out var result);\r\n_tcs.SetResult(result); \/\/ The caller gets the result back<\/pre>\n<p>This &#8220;implementation&#8221; has a few serious drawbacks:<\/p>\n<ul>\n<li>Lots of heap allocations: 1 allocation for the state machine, 1 allocation for <code>TaskCompletionSource&lt;T&gt;<\/code>, 1 allocation for task inside a <code>TaskCompletionSource&lt;T&gt;<\/code>, 1 allocation for continuation delegate.<\/li>\n<li>Lack of &#8220;hot path optimizations&#8221;: if the awaited task was already finished there is no reason to create a continuation.<\/li>\n<li>Lack of extensibility: the implementation is tightly coupled with Task-based classes that makes it impossible to use with other scenarios, like awaiting other types or returning types other than <code>Task<\/code> or <code>Task&lt;T&gt;<\/code>.<\/li>\n<\/ul>\n<p>Now let&#8217;s take a look at the actual async machinery to see how these concerns are addressed.<\/p>\n<h4>Async machinery<\/h4>\n<p>The overall approach the compiler takes for async method transformation, is very similar to the one mentioned above. To get the desired behavior the compiler relies on the following types:<\/p>\n<ol>\n<li>Generated state machine that acts like a stack frame for an asynchronous method and contains all the logic from the original async method (**).<\/li>\n<li><a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,5916df9e324fc0a1\"><code>AsyncTaskMethodBuilder&lt;T&gt;<\/code><\/a> that keeps the completed task (very similar to <code>TaskCompletionSource&lt;T&gt;<\/code> type) and manages the state transition of the state machine.<\/li>\n<li><a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/TaskAwaiter.cs,2c48fb3bdfc69022\"><code>TaskAwaiter&lt;T&gt;<\/code><\/a> that wraps a task and schedules continuations of it if needed.<\/li>\n<li><a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,c7b2691b131812c7\"><code>MoveNextRunner<\/code><\/a> that calls <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/IAsyncStateMachine.cs,22e8d7df64651ee3\"><code>IAsyncStateMachine.MoveNext<\/code><\/a>method in the correct execution context.<\/li>\n<\/ol>\n<p><strong>The generated state machine is a class in debug mode and a struct in release mode<\/strong>. All the other types (except <code>MoveNextRunner<\/code> class) are defined in the BCL as structs.<\/p>\n<p>(**) The compiler generates a type name like <code>&lt;YourMethodNameAsync&gt;d__1<\/code> for the state machine. To avoid name collisions the generated name contains invalid identifier characters which can&#8217;t be defined or referenced by the user. But for the sake of simplicity in all the following examples, I will use valid identifiers by replacing <code>&lt;<\/code> and <code>&gt;<\/code> characters with <code>_<\/code> and by using slightly easier to understand names.<\/p>\n<h5>The original method<\/h5>\n<p>Original &#8220;asynchronous&#8221; method creates a state machine instance, initializes it with the captured state (including <code>this<\/code> pointer if the method is not static) and then starts the execution by calling <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,454\"><code>AsyncTaskMethodBuilder&lt;T&gt;.Start<\/code><\/a> with the state machine instance passed by reference.<\/p>\n<pre class=\"lang:default decode:true \">[AsyncStateMachine(typeof(_GetStockPriceForAsync_d__1))]\r\npublic Task&lt;decimal&gt; GetStockPriceFor(string companyId)\r\n{\r\n    _GetStockPriceForAsync_d__1 _GetStockPriceFor_d__;\r\n    _GetStockPriceFor_d__.__this = this;\r\n    _GetStockPriceFor_d__.companyId = companyId;\r\n    _GetStockPriceFor_d__.__builder = AsyncTaskMethodBuilder&lt;decimal&gt;.Create();\r\n    _GetStockPriceFor_d__.__state = -1;\r\n    var __t__builder = _GetStockPriceFor_d__.__builder;\r\n    __t__builder.Start&lt;_GetStockPriceForAsync_d__1&gt;(ref _GetStockPriceFor_d__);\r\n    return _GetStockPriceFor_d__.__builder.Task;\r\n}<\/pre>\n<p><strong>Passing by reference<\/strong> is an important optimization, because a state machine tends to be fairly large struct (&gt;100 bytes) and passing it by reference avoids a redundant copy.<\/p>\n<h5>The state machine<\/h5>\n<pre class=\"lang:default decode:true \">struct _GetStockPriceForAsync_d__1 : IAsyncStateMachine\r\n{\r\n    public StockPrices __this;\r\n    public string companyId;\r\n    public AsyncTaskMethodBuilder&lt;decimal&gt; __builder;\r\n    public int __state;\r\n    private TaskAwaiter __task1Awaiter;\r\n \r\n    public void MoveNext()\r\n    {\r\n        decimal result;\r\n        try\r\n        {\r\n            TaskAwaiter awaiter;\r\n            if (__state != 0)\r\n            {\r\n                \/\/ State 1 of the generated state machine:\r\n                if (string.IsNullOrEmpty(companyId))\r\n                    throw new ArgumentNullException();\r\n \r\n                awaiter = __this.InitializeLocalStoreIfNeededAsync().GetAwaiter();\r\n \r\n                \/\/ Hot path optimization: if the task is completed,\r\n                \/\/ the state machine automatically moves to the next step\r\n                if (!awaiter.IsCompleted)\r\n                {\r\n                    __state = 0;\r\n                    __task1Awaiter = awaiter;\r\n \r\n                    \/\/ The following call will eventually cause boxing of the state machine.\r\n                    __builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);\r\n                    return;\r\n                }\r\n            }\r\n            else\r\n            {\r\n                awaiter = __task1Awaiter;\r\n                __task1Awaiter = default(TaskAwaiter);\r\n                __state = -1;\r\n            }\r\n \r\n            \/\/ GetResult returns void, but it'll throw if the awaited task failed.\r\n            \/\/ This exception is catched later and changes the resulting task.\r\n            awaiter.GetResult();\r\n            __this._stocks.TryGetValue(companyId, out result);\r\n        }\r\n        catch (Exception exception)\r\n        {\r\n            \/\/ Final state: failure\r\n            __state = -2;\r\n            __builder.SetException(exception);\r\n            return;\r\n        }\r\n \r\n        \/\/ Final state: success\r\n        __state = -2;\r\n        __builder.SetResult(result);\r\n    }\r\n \r\n    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)\r\n    {\r\n        __builder.SetStateMachine(stateMachine);\r\n    }\r\n}<\/pre>\n<p>The generated state machine looks complicated, but in essence, it is very similar to one we created by hand.<\/p>\n<p>Even though the state machine is similar to a hand-crafted one it has a few very important differences:<\/p>\n<p><strong>1. &#8220;Hot path&#8221; optimization<\/strong><\/p>\n<p>Unlike our naive approach, the generated state machine is aware that an awaited task might be completed already.<\/p>\n<pre class=\"lang:default decode:true \">awaiter = __this.InitializeLocalStoreIfNeededAsync().GetAwaiter();\r\n \r\n\/\/ Hot path optimization: if the task is completed,\r\n\/\/ the state machine automatically moves to the next step\r\nif (!awaiter.IsCompleted)\r\n{\r\n    \/\/ Irrelevant stuff\r\n \r\n    \/\/ The following call will eventually cause boxing of the state machine.\r\n    __builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);\r\n    return;\r\n}<\/pre>\n<p>If the awaited task is already finished (successfully or not) the state machine moves forward to the next step:<\/p>\n<pre class=\"lang:default decode:true \">\/\/ GetResult returns void, but it'll throw if the awaited task failed.\r\n\/\/ This exception is catched later and changes the resulting task.\r\nawaiter.GetResult();\r\n__this._stocks.TryGetValue(companyId, out result);<\/pre>\n<p><strong>This means that if all awaited tasks are already completed the entire state machine will stay on the stack<\/strong>. An async method even today could have an extremely small memory overhead if all awaited tasks are completed already or will complete synchroonously. The only remaining allocation would be for the task itself!<\/p>\n<p><strong>2. Error handling<\/strong> There is no special logic which covers faulted or canceled state of the awaited tasks. The state machine calls <code>awaiter.GetResult()<\/code> that will throw <code>TaskCancelledException<\/code> if the task was canceled or another exception if the task was failed. This is an elegant solution that works fine here because <code>GetResult()<\/code> is a bit different in terms of error handling than <code>task.Wait()<\/code> or <code>task.Result<\/code>.<\/p>\n<p>Both <code>task.Wait()<\/code> and <code>task.Result<\/code> throw <code>AggregateException<\/code> even when there is only a single exception that caused a task to fail. The reason for this is pretty simple: a task can represent not only IO-bound operations that usually have just one failure but the result of a parallel computation as well. In the latter case, the operation can have more than one error and <code>AggregateException<\/code> is designed to carry all these errors in one place.<\/p>\n<p>But <code>async\/await<\/code> pattern is designed specifically for asynchronous operations that usually have at most one error. So the language authors decided that it will make more sense if <code>awaiter.GetResult()<\/code> will &#8220;unwrap&#8221; an <code>AggregateException<\/code> and throw just the first failure. This design decision is not perfect and in one of the next posts, we&#8217;ll see when this abstraction can leak.<\/p>\n<p>The async state machine represents just one piece of the puzzle. To understand the whole picture we need to know how a state machine instance interacts with <code>TaskAwaiter&lt;T&gt;<\/code> and <code>AsyncTaskMethodBuilder&lt;T&gt;<\/code>.<\/p>\n<h4>How different pieces are glued together?<\/h4>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/06\/Async_sequence_state_machine.png\"><img decoding=\"async\" style=\"padding-top: 0px; padding-left: 0px; padding-right: 0px; border-width: 0px;\" title=\"Async_sequence_state_machine\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/06\/Async_sequence_state_machine_thumb.png\" alt=\"Async_sequence_state_machine\" width=\"1024\" height=\"429\" border=\"0\" \/><\/a><\/p>\n<p>The chart looks overly complicated but each piece is well-design and plays an important role. The most interesting collaboration is happening when an awaited task is not finished (marked with the brown rectangle in the diagram):<\/p>\n<ul>\n<li>The state machine calls <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,535\"><code>__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);<\/code><\/a> to register itself as the task&#8217;s continuation.<\/li>\n<li>The builder makes sure that when the task is finished a <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/IAsyncStateMachine.cs,25\"><code>IAsyncStateMachine.MoveNext<\/code><\/a> method gets called:\n<ul>\n<li>The builder <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,916\">captures<\/a> the current <code>ExecutionContext<\/code> and creates a <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,049bf1fe30f53fe3\"><code>MoveNextRunner<\/code><\/a> instance to associate it with the current state machine instance. Then it creates an <code>Action<\/code> instance from <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/runtime\/compilerservices\/AsyncMethodBuilder.cs,8539e615974ae806\"><code>MoveNextRunner.Run<\/code><\/a> that will move the state machine forward under the captured execution context.<\/li>\n<li>The builder calls <code>TaskAwaiter.UnsafeOnCompleted(action)<\/code>that schedules a given action as a continuation of an awaited task.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>When the awaited task completes, the given callback is called and the state machine runs the next code block of the asynchronous method.<\/p>\n<h5>Execution Context<\/h5>\n<p>One may wonder: what is the execution context and why we need all that complexity?<\/p>\n<p>In the synchronous world, each thread keeps ambient information in a thread-local storage. It can be security-related information, culture-specific data, or something else. When 3 methods are called sequentially in one thread this information flows naturally between all of them. But this is no longer true for asynchronous methods. Each &#8220;section&#8221; of an asynchronous method can be executed in different threads that makes thread-local information unusable.<\/p>\n<p>Execution context keeps the information for one logical flow of control even when it spans multiple threads.<\/p>\n<p>Methods like <code>Task.Run<\/code> or <code>ThreadPool.QueueUserWorkItem<\/code> do this automatically. <code>Task.Run<\/code> method captures <code>ExecutionContext<\/code> from the invoking thread and stores it with the <code>Task<\/code> instance. When the <code>TaskScheduler<\/code> associated with the task runs a given delegate, it runs it via <code>ExecutionContext.Run<\/code> using the stored context.<\/p>\n<p>We can use <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/threading\/asynclocal.cs,ef9ce034697240ba\"><code>AsyncLocal&lt;T&gt;<\/code><\/a> to demonstrate this concept in action:<\/p>\n<pre class=\"lang:default decode:true \">static Task ExecutionContextInAction()\r\n{\r\n    var li = new AsyncLocal&lt;int&gt;();\r\n    li.Value = 42;\r\n \r\n    return Task.Run(() =&gt;\r\n    {\r\n        \/\/ Task.Run restores the execution context\r\n        Console.WriteLine(\"In Task.Run: \" + li.Value);\r\n    }).ContinueWith(_ =&gt;\r\n    {\r\n        \/\/ The continuation restores the execution context as well\r\n        Console.WriteLine(\"In Task.ContinueWith: \" + li.Value);\r\n    });\r\n}<\/pre>\n<p>In these cases, the execution context flows through <code>Task.Run<\/code> and then to <code>Task.ContinueWith<\/code> method. So if you run this method you&#8217;ll see:<\/p>\n<pre class=\"lang:default decode:true\">In Task.Run: 42\r\nIn Task.ContinueWith: 42<\/pre>\n<p>But not all methods in the BCL will automatically capture and restore the execution context. Two exceptions are <code>TaskAwaiter&lt;T&gt;.UnsafeOnComplete<\/code>and <code>AsyncMethodBuilder&lt;T&gt;.AwaitUnsafeOnComplete<\/code>. It looks weird that the language authors decided to add &#8220;unsafe&#8221; methods to flow the execution context manually using <code>AsyncMethodBuilder&lt;T&gt;<\/code> and <code>MoveNextRunner<\/code>instead of relying on a built-in facilities like <a href=\"http:\/\/referencesource.microsoft.com\/#mscorlib\/system\/threading\/Tasks\/TaskContinuation.cs,543\"><code>AwaitTaskContinuation<\/code><\/a>. I suspect there were some performance reasons or anther restrictions on the existing implementation.<\/p>\n<p>Here is an example that demonstrates the difference:<\/p>\n<pre class=\"lang:default decode:true \">static async Task ExecutionContextInAsyncMethod()\r\n        {\r\n            var li = new AsyncLocal&lt;int&gt;();\r\n            li.Value = 42;\r\n            await Task.Delay(42);\r\n \r\n            \/\/ The context is implicitely captured. li.Value is 42\r\n            Console.WriteLine(\"After first await: \" + li.Value);\r\n \r\n            var tsk2 = Task.Yield();\r\n            tsk2.GetAwaiter().UnsafeOnCompleted(() =&gt;\r\n            {\r\n                \/\/ The context is not captured: li.Value is 0\r\n                Console.WriteLine(\"Inside UnsafeOnCompleted: \" + li.Value);\r\n            });\r\n \r\n            await tsk2;\r\n \r\n            \/\/ The context is captured: li.Value is 42\r\n            Console.WriteLine(\"After second await: \" + li.Value);\r\n        }<\/pre>\n<p>The output is:<\/p>\n<pre class=\"lang:default decode:true \">After first await: 42\r\nInside UnsafeOnCompleted: 0\r\nAfter second await: 42<\/pre>\n<h4>Conclusion<\/h4>\n<ul>\n<li>Async methods are very different from the synchronous methods.<\/li>\n<li>The compiler generates a state machine per each method and moves all the logic of the original method there.<\/li>\n<li>The generated code is highly optimized for a synchronous scenario: if all awaited tasks are completed, then the overhead of an async method is minimal.<\/li>\n<li>If an awaited task is not completed, the logic relies on a lot of helper types to get the job done.<\/li>\n<\/ul>\n<h4>References<\/h4>\n<p>If you want to learn more about execution context, I highly recommend the following two blog pots:<\/p>\n<ul>\n<li><a href=\"https:\/\/blogs.msdn.microsoft.com\/pfxteam\/2012\/06\/15\/executioncontext-vs-synchronizationcontext\/\">ExecutionContext vs SynchronizationContext<\/a> by Stephen Toub and<\/li>\n<li><a href=\"https:\/\/blog.stephencleary.com\/2013\/04\/implicit-async-context-asynclocal.html\">Implicit Async Context (&#8220;AsyncLocal&#8221;)<\/a> by Stephen Cleary<\/li>\n<\/ul>\n<p><strong>Next<\/strong>: we&#8217;ll explore an extensibility model of asynchronous methods in C#.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The async series Dissecting the async methods in C#. Extending the async methods in C#. The performance characteristics of the async methods in C#. One user scenario to rule them all. The C# language is great for developer&#8217;s productivity and I&#8217;m glad for the recent push towards making it more suitable for high-performance applications. Here [&hellip;]<\/p>\n","protected":false},"author":4004,"featured_media":37840,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6699,6697],"tags":[6695],"class_list":["post-1065","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c","category-tpl","tag-seteplia"],"acf":[],"blog_post_summary":"<p>The async series Dissecting the async methods in C#. Extending the async methods in C#. The performance characteristics of the async methods in C#. One user scenario to rule them all. The C# language is great for developer&#8217;s productivity and I&#8217;m glad for the recent push towards making it more suitable for high-performance applications. Here [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1065","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/4004"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=1065"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/1065\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=1065"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=1065"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=1065"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}