Skip to content

Commit 2a81ff0

Browse files
CopilotBillWagnergewarren
authored
Add documentation for synchronous access to asynchronous operations (#47735)
* Initial plan * Add synchronous access to async operations documentation section Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> * Move synchronous access section to end per review feedback Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> * Update docs/csharp/asynchronous-programming/async-scenarios.md * Add approach links and address review feedback Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> Co-authored-by: Bill Wagner <wiwagn@microsoft.com> Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com>
1 parent 2f9eb93 commit 2a81ff0

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

docs/csharp/asynchronous-programming/async-scenarios.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,74 @@ Avoid writing code that depends on the state of global objects or the execution
208208

209209
A recommended goal is to achieve complete or near-complete [Referential Transparency](https://en.wikipedia.org/wiki/Referential_transparency) in your code. This approach results in a predictable, testable, and maintainable codebase.
210210

211+
### Synchronous access to asynchronous operations
212+
213+
In scenarios, you might need to block on asynchronous operations when the `await` keyword isn't available throughout your call stack. This situation occurs in legacy codebases or when integrating asynchronous methods into synchronous APIs that can't be changed.
214+
215+
> [!WARNING]
216+
> Synchronous blocking on asynchronous operations can lead to deadlocks and should be avoided whenever possible. The preferred solution is to use `async`/`await` throughout your call stack.
217+
218+
When you must block synchronously on a `Task`, here are the available approaches, listed from most to least preferred:
219+
220+
- [Use GetAwaiter().GetResult()](#use-getawaitergetresult)
221+
- [Use Task.Run for complex scenarios](#use-taskrun-for-complex-scenarios)
222+
- [Use Wait() and Result](#use-wait-and-result)
223+
224+
#### Use GetAwaiter().GetResult()
225+
226+
The `GetAwaiter().GetResult()` pattern is generally the preferred approach when you must block synchronously:
227+
228+
```csharp
229+
// When you cannot use await
230+
Task<string> task = GetDataAsync();
231+
string result = task.GetAwaiter().GetResult();
232+
```
233+
234+
This approach:
235+
236+
- Preserves the original exception without wrapping it in an `AggregateException`.
237+
- Blocks the current thread until the task completes.
238+
- Still carries deadlock risk if not used carefully.
239+
240+
#### Use Task.Run for complex scenarios
241+
242+
For complex scenarios where you need to isolate the asynchronous work:
243+
244+
```csharp
245+
// Offload to thread pool to avoid context deadlocks
246+
string result = Task.Run(async () => await GetDataAsync()).GetAwaiter().GetResult();
247+
```
248+
249+
This pattern:
250+
251+
- Executes the asynchronous method on a thread pool thread.
252+
- Can help avoid some deadlock scenarios.
253+
- Adds overhead by scheduling work to the thread pool.
254+
255+
#### Use Wait() and Result
256+
257+
You can use a blocking approach by calling <xref:System.Threading.Tasks.Task.Wait> and <xref:System.Threading.Tasks.Task`1.Result>. However, this approach is discouraged because it wraps exceptions in <xref:System.AggregateException>.
258+
259+
```csharp
260+
Task<string> task = GetDataAsync();
261+
task.Wait();
262+
string result = task.Result;
263+
```
264+
265+
Problems with `Wait()` and `Result`:
266+
267+
- Exceptions are wrapped in `AggregateException`, making error handling more complex.
268+
- Higher deadlock risk.
269+
- Less clear intent in code.
270+
271+
#### Additional considerations
272+
273+
- Deadlock prevention: Be especially careful in UI applications or when using a synchronization context.
274+
- Performance impact: Blocking threads reduces scalability.
275+
- Exception handling: Test error scenarios carefully as exception behavior differs between patterns.
276+
277+
For more detailed guidance on the challenges and considerations of synchronous wrappers for asynchronous methods, see [Should I expose synchronous wrappers for asynchronous methods?](https://devblogs.microsoft.com/pfxteam/should-i-expose-synchronous-wrappers-for-asynchronous-methods/).
278+
211279
## Review the complete example
212280

213281
The following code represents the complete example, which is available in the *Program.cs* example file.

0 commit comments

Comments
 (0)