In C#, the asynchronous programming can be achieved using the async/await, Async/Await is especially important to use in .NET and .NET Core web servers if high concurrency is desired. .NET uses a managed thread pool that will only spawn up to something like 50 threads quickly by default. If it sees that more threads are needed, it will slowly add them. So in this post, I am going to explain how you can use async-await keyword in C# with example.

What is an async method?

An async method is a method that returns to the calling method before completing all its work, and then completes its work while the calling method continues its execution.

An async method has the following characteristics:

  • An async method must have the async keyword in its method header, and it must be before the return type.
  • This modifier doesn’t do anything more than signal that the method contains one or more await expressions.
  • It contains one or more await expressions. These expressions represent tasks that can be done asynchronously.
  • It must have one of the following three return types.
    1.  void :If the calling method just wants the async method to execute, but doesn’t need any further interaction with it
    2. Task : If the calling method doesn’t need a return value from the async method, but needs to be able to check on the async method’s state
    3. Task<T> :If the calling method is to receive a value of type T back from the call, the return type of the async method must be Task
  • An async method can have any number of formal parameters of any types but it cannot be out or ref parameters.
  • The name of an async method should end with the suffix Async.
  • Other than Methods, lambda expressions and anonymous methods can also act as async objects.

Benefit of using Async

Asynchrony is essential for activities that are potentially blocking, such as web access. Access to a web resource sometimes is slow or delayed. If such an activity is blocked in a synchronous process, the entire application must wait. In an asynchronous process, the application can continue with other work that doesn't depend on the web resource until the potentially blocking task finishes.

Let's take a look at an example using Console application:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitInCsharp
{
    class Program
    {
        static void Main(string[] args)
        {
            DoAsyncWork.CalculateSumAsync(10, 11);
            //do some other work.
            Thread.Sleep(200);
            Console.WriteLine("Program Exiting");
            Console.ReadLine();
        }

        static class DoAsyncWork
        {
            public static async void CalculateSumAsync(int i1, int i2)
            {
                int sum = await Task.Run(() => GetSum(i1, i2));
                Console.WriteLine("Value: {0}", sum);
            }

            private static int GetSum(int i1, int i2)
            {
                return i1 + i2;
            }
        }
    }
}

async-await-in-csharp-min.png

Await keyword is used to call async method, which marks a point where the method can't continue until the awaited asynchronous operation is complete. In the meantime, the method is suspended, and control returns to the method's caller. 

An async method typically contains one or more occurrences of an await operator, but the absence of await expressions doesn’t cause a compiler error. If an async method doesn’t use an await operator to mark a suspension point, the method executes as a synchronous method does, despite the async modifier. The compiler issues a warning for such methods.

The task to which the await operator is applied typically is returned by a call to a method that implements the Task-Based Asynchronous Pattern. They include methods that return Task, Task<TResult>, and System.Threading.Tasks.ValueType<TResult> objects.

Let consider one more example, in this example, the HttpClient.GetByteArrayAsync method returns a Task<byte[]>. The task is a promise to produce the actual byte array when the task is complete. The await operator suspends execution until the work of the GetByteArrayAsync method is complete. In the meantime, control is returned to the caller of GetPageSizeAsync. When the task finishes execution, the await expression evaluates to a byte array.

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class AsyncAwaitInCsharp
{
   public static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length > 1)
         GetPageSizeAsync(args[1]).Wait();
      else
         Console.WriteLine("Enter at least one URL on the command line.");
   }

   private static async Task GetPageSizeAsync(string url)  
   {  
       var client = new HttpClient();  
       var uri = new Uri(Uri.EscapeUriString(url));
       byte[] urlContents = await client.GetByteArrayAsync(uri);
       Console.WriteLine($"{url}: {urlContents.Length/2:N0} characters");  
   }  
}
// The following call from the command line:
//    await1 http://docs.microsoft.com
// displays output like the following: 
//   http://docs.microsoft.com: 7,967 characters

As shown in the previous example, if await is applied to the result of a method call that returns a Task<TResult>, then the type of the await expression is TResult. If await is applied to the result of a method call that returns a Task, then the type of the await expression is void. The following example illustrates the difference.

// await keyword used with a method that returns a Task<TResult>.  
TResult result = await AsyncMethodThatReturnsTaskTResult();  
  
// await keyword used with a method that returns a Task.  
await AsyncMethodThatReturnsTask();  

// await keyword used with a method that returns a ValueTask<TResult>.
TResult result = await AsyncMethodThatReturnsValueTaskTResult();

An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task. Control then returns to the caller of the async method. When the task completes, it invokes its continuation, and execution of the async method resumes where it left off.

An await expression can occur only in the body of its enclosing method, lambda expression, or anonymous method, which must be marked with an async modifier. The term await serves as a keyword only in that context. Elsewhere, it is interpreted as an identifier. Within the method, lambda expression, or anonymous method, an await expression cannot occur in the body of a synchronous function, in a query expression, in the block of a lock statement, or in an unsafe context.