Async Await in C#.NET – Guidelines and Best Practices
Today in this article we will learn best practices for Async Await in C#.NET.
When working with asynchronous programming in .NET using the async
and await
keywords, it’s important to follow best practices to ensure efficient and maintainable code.
Here are some best practices for working with async
and await
in .NET.
- Use async and await throughout the code workflow
- Avoid Async Void
- Prefer Task Return Types
- Configure Awaits Correctly
- Use Task.WhenAll or Task.WhenAny
- Handle Exceptions Appropriately
- Utilize CancellationToken
- Do not mix Blocking and Non-Blocking Code
- Avoid CPU-Bound Operations in Async Methods
- Understand Synchronization Context
- Summary
Use async and await throughout the code workflow
To get the best out of your code workflow this is an important aspect.
Use must use async
and await
throughout your codebase consistently and without missing any place to get full execution of the asynchronous nature of method execution.
Any miss of calling method/services will make the method a blocking method causing synchronous code execution of code.
Apply async
to methods that perform long-running or I/O-bound operations, and use await
to asynchronously wait for the results of those operations.
This allows for non-blocking execution and better utilization of system resources.
Avoid Async Void
Avoid using async void
methods except for event handlers, as they can lead to unhandled exceptions that are difficult to catch and handle properly.
Instead, prefer using async
Task
or async Task<T>
for methods that can be awaited.
Prefer Task Return Types
Use Task
or from a Task”>Task<T>
as the return type for async
methods whenever possible.
This allows callers to await the method and handle exceptions or perform additional operations asynchronously.
Example :
[HttpPost]
public async Task<IActionResult> SendSms1(NotifyRequest request)
{
return Ok(await A());
}
private async Task<bool> A()
{
return await B();
}
Configure Awaits Correctly
Use ConfigureAwait(false) when awaiting tasks within library code that don’t require synchronization context.
This helps avoid unnecessary overhead by not capturing the current synchronization context.
Example:
Note: ASP.NET Core doesn’t have any SynchronizationContext so it is preferable to use ConfigureAwait(false)
Use Task.WhenAll or Task.WhenAny
When you need to await multiple tasks concurrently, use Task.WhenAll
to wait for all tasks to be completed, or Task.WhenAny
to wait for the first completed task.
When used method asynchronously awaits multiple asynchronous operations, until all operations are completed.
It’s a very good means of handling a few collections of the Tasks and getting their results together as completion criteria.
Let’s use WhenAll to await the completion of all the running Tasks.
EmployeeDetails[] employeeList = awaitTask.WhenAll(completedTask);
This allows for better parallelism and can improve overall performance.
Handle Exceptions Appropriately
Use try-catch
blocks to handle exceptions that may occur within async
methods.
Use the await
operator inside the try
block and handle any exceptions in the catch
block.
Properly log and handle exceptions to prevent application crashes and maintain code robustness.
Utilize CancellationToken
Pass a CancellationToken
to async
methods when possible to support cancellation.
This allows for better responsiveness and the ability to cancel long-running operations when needed.
static async Task<bool> ProcessAsync(HttpClient client, CancellationToken token) { HttpResponseMessage response = await client.GetAsync(url, token); byte[] content = await response.Content.ReadAsByteArrayAsync(token); //do something return true; }
Do not mix Blocking and Non-Blocking Code
Avoid blocking on asynchronous code by using blocking calls like Task.Wait
or Task.Result
.
Mixing blocking and non-blocking code can lead to deadlocks and reduce the benefits of asynchronous programming.
Avoid CPU-Bound Operations in Async Methods
Avoid performing CPU-bound operations directly in async
methods.
Instead, offload such operations to dedicated worker threads or use parallel processing techniques like Parallel.ForEach
to maximize performance.
Example :
private static IList<int> GetAllPrimeNumbers(IList<int> numbers) { Parallel.ForEach(numbers, number => { if (IsPrime(number)) { primeNumbers.Add(number); } }); return primeNumbers.ToList(); }
Understand Synchronization Context
Be aware of the synchronization context in which your async
code is executing. Understand the implications of different contexts, such as UI thread synchronization context in GUI applications.
Example: ASP.NET Core doesn’t need to worry about synchronization as it doesn’t have any SynchronizationContext.
Summary
Today in this article we discussed the best practice of using async and await keywords using which we can develop clean, efficient, and maintainable asynchronous code using async and await in .NET.
Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.