-
Notifications
You must be signed in to change notification settings - Fork 771
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
base: master
Are you sure you want to change the base?
Changes from all commits
e5857bf
70f50cd
7dd2d69
c7141c9
119d175
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -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. | ||||||||
|
||||||||
```C# | ||||||||
public async Task<string> Get(int id) | ||||||||
{ | ||||||||
HttpResponseMessage result; | ||||||||
using(var client = new HttpClient()) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
{ | ||||||||
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()) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
{ | ||||||||
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()) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
{ | ||||||||
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 | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
||||||||
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. | ||||||||
|
There was a problem hiding this comment.
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.