C# programming
Asynchronous Programming
Synchronous Method
Coding Techniques
Software Development

How to call asynchronous method from synchronous method in C#?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Calling an asynchronous method from a synchronous method in C# is a scenario developers often face, especially when dealing with legacy codebases or APIs that require non-blocking calls. While the task isn't straightforward due to the potential for deadlocks and performance issues, there are certain patterns and best practices that can help manage this challenge.

Understanding the Need

Asynchronous programming allows you to perform resource-intensive operations without blocking the execution thread. The .NET framework provides asynchronous versions of many I/O operations, such as file access, database calls, or network requests. These operations can be performed without creating additional threads, which is beneficial for resource conservation and application performance.

However, there are times when an asynchronous method needs to be called from a synchronous method. This scenario can arise in various contexts, especially when integrating new asynchronous code with older synchronous code, or when third-party libraries only provide synchronous methods.

Challenges Involved

  1. Deadlocks: A common issue when calling an async method from a sync method is the potential for deadlock. This typically happens when the synchronous method is waiting on an async operation that needs a thread which is occupied by the synchronous method itself.
  2. Thread Pool Exhaustion: Improper handling can lead to excessive consumption of thread pool resources, affecting the overall performance of the application.
  3. Design Inconsistencies: Asynchronous programming models are fundamentally different from synchronous ones. Mixing them without careful consideration can lead to maintenance and scalability issues.

Techniques to Call Asynchronous Methods from Synchronous Methods

Using Task.Run()

Often considered an easier approach, Task.Run() offloads the asynchronous operation onto another thread from the thread pool, allowing the synchronous method to continue executing.

csharp
1public void MySynchronousMethod()
2{
3    Task.Run(async () => await MyAsynchronousMethod()).Wait();
4}

However, this approach should be used cautiously as it involves thread handling and can lead to performance overhead.

Using ConfigureAwait(false)

When awaiting on tasks within an asynchronous method called from a synchronous method, using ConfigureAwait(false) prevents the continuations from attempting to marshal back to the original context.

csharp
1public async Task MyAsynchronousMethod()
2{
3    await Task.Delay(1000).ConfigureAwait(false);
4    // Further async operations
5}
6
7public void MySynchronousMethod()
8{
9    Task.Run(async () => await MyAsynchronousMethod()).Wait();
10}

This method helps in avoiding deadlocks, especially in UI applications or when dealing with the current synchronization context.

Using the GetAwaiter().GetResult()

For developers who need a method to complete and return a result within a synchronous process, GetAwaiter().GetResult() can be used. This method waits for the Task to complete and returns the result, throwing an exception if the Task fails.

csharp
1public void MySynchronousMethod()
2{
3    var result = MyAsynchronousMethod().GetAwaiter().GetResult();
4}

This technique is prone to deadlocks, similar to .Wait(), but useful in console applications or where the context is not an issue.

Using AsyncHelper

Building a helper class for handling these scenarios can abstract some of the complexities:

csharp
1public static class AsyncHelper
2{
3    public static void RunSync(Func<Task> task)
4    {
5        var oldContext = SynchronizationContext.Current;
6        var synch = new SynchronizationContext();
7
8        SynchronizationContext.SetSynchronizationContext(synch);
9
10        try
11        {
12            synch.OperationStarted();
13            task().ContinueWith(t => synch.OperationCompleted(), TaskScheduler.Default).Wait();
14        }
15        finally
16        {
17            SynchronizationContext.SetSynchronizationContext(oldContext);
18        }
19    }
20}

This custom utility handles the context switch and ensures tasks are completed without causing deadlocks.

Summary Table of Approaches

MethodUse CaseProsCons
Task.Run()General use in server applicationsSimple, offloads to a thread poolPotential performance overhead
ConfigureAwait(false)In libraries or UI applicationsAvoids deadlocksStill requires careful handling
GetAwaiter().GetResult()Where result needs to be fetchedDirect and straightforwardRisk of deadlocks
AsyncHelperAcross various application typesAbstracts complexity, reusableMore complex to implement

Conclusion

Integrating asynchronous methods into synchronous code requires understanding of both paradigms. While it's ideal to refactor synchronous methods into asynchronous ones where possible, using the above methods allows for effective utilization of async methods within a synchronous context, avoiding common pitfalls like deadlocks and thread exhaustion. Always choose the technique that aligns with the specific requirements and constraints of your application architecture.


Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.