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

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:

Async Await in CNET Guidelines and Best Practices

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.



Leave a Reply

Your email address will not be published. Required fields are marked *