How YOU can make your .NET programs more responsive with async/await in .NET Core, C# and VS Code
Tags: dotnet, tutorial, csharp, showdev
Published: 2019-11-12 20:37:10

---
title: How YOU can make your .NET programs more responsive with async/await in .NET Core, C# and VS Code
published: true
description: This describes how to make your code faster and more responsive by learning to code asynchronously with Tasks and utilizing existing async APIs
tags: dotnet, tutorial, csharp, showdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/0yr9tckstlb2m5re3c4u.jpg
canonical_url: https://softchris.github.io/pages/dotnet-tasks.html
---

Follow me on [Twitter](https://twitter.com/chris_noring), happy to take your suggestions on topics or improvements /Chris

> When we run synchronous code we block the main Thread from doing anything else than just what's it's doing currently. This makes your software and user experience slower than it needs to be. 

TLDR; we have the concept of Threads in .NET/.NET Core and they are an excellent way to schedule work to be carried out in parallel. However, they might be cumbersome to use. There is, however, a library called TPL, **T**ask **P**arallel **L**ibrary that lives on top of the Thread model and makes it really easy to schedule and manage work.  

## References

- [Async return types](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/async-return-types?wt.mc_id=devto-blog-chnoring)
It gives you a good intro to Tasks.

- [Task Control Flows](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/control-flow-in-async-programs?wt.mc_id=devto-blog-chnoring)
This talks about Control flows, how to ensure that the code happens in the right order

- [Task Based Asynchronous programming](https://docs.microsoft.com/dotnet/standard/parallel-programming/task-based-asynchronous-programming?wt.mc_id=devto-blog-chnoring)
This is more of an overview of Task-based programming

- [How to run Tasks sequentially](https://docs.microsoft.com/dotnet/standard/parallel-programming/chaining-tasks-by-using-continuation-tasks?wt.mc_id=devto-blog-chnoring)
This talks about how to run Tasks in order, one after another.

- [Task Cancellation](https://docs.microsoft.com/dotnet/standard/parallel-programming/task-cancellation?wt.mc_id=devto-blog-chnoring)
This teaches you how to cancel and listen for cancellation messages for your tasks
- [Durable Functions in Azure](https://docs.microsoft.com/azure/azure-functions/durable/durable-functions-overview?wt.mc_id=devto-blog-chnoring)
This shows how to do Tasks in Serverless programming and specifically Durable Functions
- [A Recipe converting sync code to async](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/walkthrough-accessing-the-web-by-using-async-and-await?wt.mc_id=devto-blog-chnoring)
This takes you all the way from synchronous code to gradually convert it to asynchronous code.

## WHAT

So we mentioned TPL as a library. What do we need to know? TPL is such a central and important concept that it lives in the core APIs. It's part of the `System.Threading` and `System.Threading.Tasks` namespaces. It does a lot for us like:

- **Partitioning** of the work 
- **Scheduling** of threads on the `ThreadPool`
- **Cancellation** support
- **State** management

and other low-level details.

There are some basic concepts that we need to understand.

- **Task**, a task represent an asynchronous operation, like fetching content from a file or doing a calculation that takes time. There are some interesting properties on a Task that allows us to communicate to a UI, for example, how the asynchronous work is doing, like: 
  - **Status**, this can tell us if it's currently working on something, is done, errored out or it was canceled
  - **IsCanceled**, if canceled this would be set to `true` 
  - **IsFaulted**, if something went wrong, like an exception, this would be set to `true` 
  - **IsCompleted**, once it has finished its operation it would be set to `true` 
- **Async/Await**. The `await` keyword means that we wait for the asynchronous operation to end and by the end of the operation we are given the result, e.g `var fileContent = await GetFileAsync()`. Any method that uses the `await` concept would need to have `async` keyword as part of the method header.
- **Blocking/Non-blocking**. When we use Tasks we are not blocking and other Threads can carry out work. There are exceptions though when we use the method `Wait()` on a Task. Then we are forcing the code to run synchronously. We will show that in our demo in the next section.

## WHY

A lot of things like opening up large files or carrying out a Web Request or maybe searching through your computer - are things that can be done in *parallel*. This means you can return back to the user much faster with a result and your app will be perceived as faster and more responsive. Web Development already uses the concept of *Tasks* heavily, which is a central concept in TPL. Learning how to use TPL can really make your applications more responsive. My hope is that you with this article feel more empowered to use TPL and Tasks.

## DEMO

In our Demo we will demonstrate the following:

- **Authoring** methods, How to author methods using `async/await` and how to return different types
- **Control flow**, we will show how to wait for all as well as specific Tasks
-  **Blocking code**, we will show how the usage of `Result` as well as `Wait()` affects your code

### Scaffold a project

Let's start by creating a solution like so:

```
mkdir tasks
cd tasks

dotnet new sln
```

This should create a solution file. 

Next, we will create a console project like so:

```
dotnet new solution -o task-demo
```

and now add it to the solution like so:

```
dotnet sln add task-demo/task-demo.csproj
```

Ok, we are ready to start coding. Open up an IDE, I'm gonna go with VS Code.

### Authoring methods

Let's open up the file `Program.cs` and add the following method inside of the class `Program`:

```csharp
static async Task<int> Sum(int a, int b) 
{
    var result = await Task.FromResult(a + b);
    return result;
}
```

There are some interesting things that go on above:

- **Return type**, `Task<int>`. This tells us that it will be a Task that once resolved will return something of type `int`. 
- `Task.FromResult()`, This creates a Task given a value. We give it the calculation to perform, e.g `a+b`.
- **Async/Await**, We can see how we use the `async` keyword inside of the method to wait for the result to arrive back to us. This needs to be followed by the `async` keyword to ensure the compiler is happy.

It's easy to think that the above method above doesn't need to be asynchronous but imagine instead that this is a calculation that takes time, then it would make more sense.

One other thing, `Task.FromResult` is used when the answer is immediately known so it's status is `RanToCompletion` and the answer is available right away so you can argue that `await` is unnecessary, it's already available on the `Task.Result` property. Another way to do the above is:

```csharp
static async Task<int> Sum2(int a, int ab) 
{
  var result = await Task.Run(() => {
    // do some time-consuming work
    return a + b;
  })
  return result;
}

await Sum2(1,2)
```

### Control flow

There's more to Tasks than just marking them `async`. We can ensure to wait for *all* or some of the tasks to finish before carrying on with our code. We have some constructs that help us control this flow:

- `Task.WaitAll()`, this one takes a list of Tasks in. What you are essentially saying is that all tasks need to finish before we can carry on, it's blocking. You can see that by it returning `void`
A typical use-case is to wait for all Web Requests to finish cause we want to return a result that consists of us stitching all their data together
- `Task.WaitAny()`, we give it a list of Tasks here as well but the meaning is different. We say that as long as any of the Task has finished we are good. This usually a race for data towards an endpoint or search for a file/file content on a disk. We don't care who finished first, as long as we get *a response*. This is also blocking and waiting for one of the Tasks to finish
- `Task.WhenAll()`, this gives you a `Task` back that you can interact with. When all of the tasks have finished it will resolve.  
- `Task.WhenAny()`, this gives you a `Task` back that you can interact with. When one of the Tasks has finished then it will resolve. 

Let's create a demo of a Control flow. We will fake carrying out time-consuming work by adding an additional method to our class, like so:

```csharp
static async Task DoSomething()
{
  await Task.Delay(2000);
}
```

**Demo - Control flow**

Now we can add some control flow code in our `Main()` method like so:

```csharp
var start  = DateTime.Now;

var taskSum = Sum(2,2);
var taskDelay = DoSomething();
Task.WaitAll(taskSum, taskDelay);

end = DateTime.Now;

Console.WriteLine("Time taken {0}",end - start);
```

Our full code in `Program.cs` should now look like this:

```csharp
using System;
using System.Threading.Tasks;
using System.IO;

namespace task_demo
{
    class Program
    {
        static async Task DoSomething()
        {
            await Task.Delay(2000);
        }
        static async Task<int> Sum(int a, int b) 
        {
            var result = await Task.FromResult(a + b);
            return result;
        }

        static void Main(string[] args)
        {
            var start  = DateTime.Now;

            var taskSum = Sum(2,2);
            var taskDelay = DoSomething();

            Task.WaitAll(taskSum, taskDelay);

            end = DateTime.Now;

            Console.WriteLine("Time taken! {0}", end-start);
        }
    }
}

```

Let' compile:

```
dotnet build
```

and run it:

```
dotnet run
```

We should get the following response:

```
4
Time taken! 00:00:02.0026920
```

Even though the calculation from calling `Sum()` took a few milliseconds, we don't get any response until 2 seconds later, when `DoSomething()` has finished.

If we shift our code now from `WaitAll` to `WhenAll` we would get very different behavior. The code would have kept going and reported this instead:

```
4
Time taken! 00:00:00.0235860
```

So the lesson here is that if we want the code to wait at a specific point, using `WaitAny` is a good idea but if you want to start up a lot of asynchronous work then use `When...`. 

We can still make the code behave correctly with `WhenAll` but we would need to investigate the status like so:

```csharp
var twoTasks = Task.WhenAll(taskSum, taskDelay);
if(twoTasks.IsCompleted) 
{
    var end = DateTime.Now;
    Console.WriteLine("{0}", taskSum.Result);
}
```

**DEMO - Wait any**

To test this one out we create three new methods that mock opening up files. Each of the three methods has a delay built in that differs:

```csharp
static async Task<string> ReadFile1() 
{
    await Task.Delay(3000);
    return "file1";
}

static async Task<string> ReadFile2()
{
    await Task.Delay(4000);
    return "file2";
}

static async Task<string> ReadFile3()
{
    await Task.Delay(2000);
    return "file3";
}
```

Let's update our `Program()` method with some code as well:

```csharp
var task1 = ReadFile1();
var task2 = ReadFile2();
var task3 = ReadFile3();

start = DateTime.Now;
Task.WaitAny(task1, task2, task3);


Console.WriteLine("Task1, completed: {0}", task1.IsCompleted);

Console.WriteLine("Task2, completed: {0}", task2.IsCompleted);

Console.WriteLine("Task3, completed: {0}", task3.IsCompleted);
Console.WriteLine("Task3, completed: {0}", task3.Result);

end = DateTime.Now;
Console.WriteLine("Time taken! {0}", end - start);
```

As you can see above, we are waiting for one of the three tasks to finish, with this construct:

```csharp
Task.WaitAny(task1, task2, task3);
```

Given what we know of the methods being called, `ReadFile3()` should finish first, after `2` seconds, but let's test that by running our program:

```
Task1, completed: False
Task2, completed: False
Task3, completed: True
Task3, completed: file3
Time taken! 00:00:02.0031370
```

We can see above that `Task3` is completed and the other tasks haven't completed yet. 

### Using Async APIs

Ok, we now understand more about async and is able to leverage that on existing APIs. Let's look at reading the content of a file. Normally you would create a method like so:

```csharp
 static async string ReadTxtFile() 
{
    using(var sr = new StreamReader(File.Open("test.txt", FileMode.Open)))
    {
        return sr.ReadToEnd();
    }
}
```

The above would block though and you wouldn't be able to do much else while this finishes. Imagine this is a really large file then it would be really noticeable. If we rewrite the method to use an *async* version we would instead get code looking like this:

```csharp
 static async Task<string> ReadTxtFile() 
{
    using(var sr = new StreamReader(File.Open("test.txt", FileMode.Open)))
    {
        return await sr.ReadToEndAsync();
    }
}
```

This doesn't block and everyone is happy.

### Blocking code

One of the tricky parts of using TPL is knowing what calls block. You are all happy that your code is now asynchronous but suddenly you end up blocking anyway. So what shall we look out for? Well, we touched upon this subject already:

- `WaitAll` and `WaitAny` blocks, the rule of thumb here seems to be that they return void and use the word *Wait...*. Sometimes you want it to wait though, so learn to be intentional with block/non-block
- `task.Result`, this also blocks and waits for the result to be available
- `Wait()`, this method on a Task will block and cause you to wait here until the code has finished, for example `Task.Delay(2000).Wait()`

### Full code

This is the full code I was playing around with if you want to explore for yourself:

```csharp
using System;
using System.Threading.Tasks;
using System.IO;

namespace task_demo
{
  class Program
  {
      static async Task<string> ReadTxtFile() 
      {
          using(var sr = new StreamReader(File.Open("test.txt", FileMode.Open)))
          {
              return await sr.ReadToEndAsync();
          }
      }

      static string ReadFileSync1() 
      {
          Task.Delay(2000).Wait();
          return "content1";
      }

      static string ReadFileSync2()
      {
          Task.Delay(2000).Wait();
          return "content2";
      }

      static string ReadFileSync3()
          {
          Task.Delay(2000).Wait();
          return "content3";
      }
      
      static async Task DoSomething()
      {
          await Task.Delay(2000);
      }
      static async Task<int> Sum(int a, int b) 
      {
          var result = await Task.FromResult(a + b);
          return result;
      }

      static async Task<string> ReadFile1() 
      {
          await Task.Delay(3000);
          return "file1";
      }

      static async Task<string> ReadFile2()
      {
          await Task.Delay(4000);
          return "file2";
      }

      static async Task<string> ReadFile3()
      {
          await Task.Delay(2000);
          return "file3";
      }

      static void Main(string[] args)
      {
          var start = DateTime.Now;
          var c1 = ReadFileSync1();
          var c2 = ReadFileSync2();
          var c3 = ReadFileSync3();
          var end = DateTime.Now;
          Console.WriteLine("Time taken {0}", end-start);

          start  = DateTime.Now;

          var taskSum = Sum(2,2);
          var taskDelay = DoSomething();

          Task.WaitAll(taskSum, taskDelay);

          end = DateTime.Now;

          Console.WriteLine("{0}",taskSum.Result);

          Console.WriteLine("Time taken! {0}", end-start);

          var task1 = ReadFile1();
          var task2 = ReadFile2();
          var task3 = ReadFile3();

          start = DateTime.Now;
          Task.WaitAny(task1, task2, task3);
          
          
          Console.WriteLine("Task1, completed: {0}", task1.IsCompleted);
          
          // this forces everyone to wait for this Task1
          // Console.WriteLine("Task1, completed: {0}", task1.Result);

          Console.WriteLine("Task2, completed: {0}", task2.IsCompleted);

          Console.WriteLine("Task3, completed: {0}", task3.IsCompleted);
          Console.WriteLine("Task3, completed: {0}", task3.Result);

          end = DateTime.Now;
          Console.WriteLine("Time taken! {0}", end - start);

      }
  }
}

```

## Summary

In summary, we learned about the concept of Tasks and their anatomy. Additionally, we learned about Control Flows and we also discussed blocking/non-blocking code. There is more to learn though like how to cancel Tasks. Im gonna save that one for a separate article. I will add a link to Cancellation in the References section of this article.