Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add notes for ConfigureAwait. #61

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 75 additions & 1 deletion AsyncGuidance.md
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,82 @@ public async Task<int> DoSomethingAsync()

## ConfigureAwait

TBD
`ConfigureAwait` changes whether the original thread context or thread scheduler is used when resuming a method after `await`.

In practice, this means that code executing after `await` may or may not have access to global objects like `HttpContext`, or `SynchronizationContext.Current` may not be on the UI thread of a WPF application.

`ConfigureAwait` takes a boolean value of `true` or `false`:
* `ConfigureAwait(true)` will cause the async method to capture the context. This is the default behavior; `ConfigureAwait(true)` is rarely used.
* `ConfigureAwait(false)` will cause the async method to not capture the context.

Prefer `ConfigureAwait(false)` over the default behavior. Like `CancellationTokens`, this could be considered a cooperative effort.
By not capturing the thread context or scheduler, less memory is used, and fewer CPU cycles are used when continuations runs.
It also frees the application from having to run continuations through a `SynchronizationContext`.


:bulb:**NOTE ASP.NET Core does not have a `SynchronizationContext` and does not have this problem.**

:bulb:**NOTE Some advice states to use `ContinueAwait(false)` to avoid deadlocks. This is mistaken advice and does [not guarantee against deadlocks](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html).**

❌ **BAD** This example uses a global object and throws a `NullReferenceException` during runtime.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove the ASP.NET examples (non core) as there's nothing else in this article that tries to explain that.


```C#
public async Task<string> Get(int id)
{
HttpResponseMessage result;
using(var client = new HttpClient())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using(var client = new HttpClient())
using (var client = new HttpClient())

{
result = await client.GetAsync("http://example.org").ConfigureAwait(false);
}

var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false);

// HttpContext is null
var username = HttpContext.Current.Session["username"];

return $"{username}, your requested content is {content}";
}
```

:white_check_mark: **GOOD** This example avoids the `NullReferenceException` by moving the `HttpContext` access above the `await` calls.

```C#
public async Task<string> Get(int id)
{
// HttpContext is not null because no awaits with ConfigureAwait(false) have executed
var username = HttpContext.Current.Session["username"];

HttpResponseMessage result;
using(var client = new HttpClient())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using(var client = new HttpClient())
using (var client = new HttpClient())

{
result = await client.GetAsync("http://example.org").ConfigureAwait(false);
}

var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false);

return $"{username}, your requested content is {content}";
}
```

:white_check_mark: **GOOD** This example avoids the `NullReferenceException` by not using `ConfigureAwait(false)`.

```C#
public async Task<string> Get(int id)
{
HttpResponseMessage result;
using(var client = new HttpClient())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using(var client = new HttpClient())
using (var client = new HttpClient())

{
result = await client.GetAsync("http://example.org");
}

var content = await result.Content.ReadAsStringAsync();

// HttpContext is null
var username = HttpContext.Current.Session["username"];

return $"{username}, your requested content is {content}";
}
```
# Scenarios
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Scenarios
# Scenarios


The above tries to distill general guidance, but doesn't do justice to the kinds of real-world situations that cause code like this to be written in the first place (bad code). This section tries to take concrete examples from real applications and turn them into something simple to help you relate these problems to existing codebases.
Expand Down