Understanding async / await in C#

C#Async Await

C# Problem Overview


I'm starting to learn about async / await in C# 5.0, and I don't understand it at all. I don't understand how it can be used for parallelism. I've tried the following very basic program:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task task1 = Task1();
            Task task2 = Task2();

            Task.WaitAll(task1, task2);

            Debug.WriteLine("Finished main method");
        }

        public static async Task Task1()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
            Debug.WriteLine("Finished Task1");
        }

        public static async Task Task2()
        {
            await new Task(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
            Debug.WriteLine("Finished Task2");
        }

    }
}

This program just blocks on the call to Task.WaitAll() and never finishes, but I am not understanding why. I'm sure I'm just missing something simple or just don't have the right mental model of this, and none of the blogs or MSDN articles that are out there are helping.

C# Solutions


Solution 1 - C#

I recommend you start out with my intro to async/await and follow-up with the official MSDN documentation on TAP.

As I mention in my intro blog post, there are several Task members that are holdovers from the TPL and have no use in pure async code. new Task and Task.Start should be replaced with Task.Run (or TaskFactory.StartNew). Similarly, Thread.Sleep should be replaced with Task.Delay.

Finally, I recommend that you do not use Task.WaitAll; your Console app should just Wait on a single Task which uses Task.WhenAll. With all these changes, your code would look like:

class Program
{
    static void Main(string[] args)
    {
        MainAsync().Wait();
    }

    public static async Task MainAsync()
    {
        Task task1 = Task1();
        Task task2 = Task2();

        await Task.WhenAll(task1, task2);

        Debug.WriteLine("Finished main method");
    }

    public static async Task Task1()
    {
        await Task.Delay(5000);
        Debug.WriteLine("Finished Task1");
    }

    public static async Task Task2()
    {
        await Task.Delay(10000);
        Debug.WriteLine("Finished Task2");
    }
}

Solution 2 - C#

Understand C# Task, async and await

C# Task

Task class is an asynchronous task wrapper. Thread.Sleep(1000) can stop a thread running for 1 second. While Task.Delay(1000) won't stop the current work. See code:

public static void Main(string[] args){
    TaskTest();
}
private static void TaskTest(){
     Task.Delay(5000);
     System.Console.WriteLine("task done");
}

When running," task done" will show up immediately. So I can assume that every method from Task should be asynchronous. If I replace TaskTest () with Task.Run(() =>TaskTest()) task done won't show up at all until I append a Console.ReadLine(); after the Run method.

Internally, Task class represent a thread state In a State Machine. Every state in state machine have several states such as Start, Delay, Cancel, and Stop.

async and await

Now, you may wondering if all Task is asynchronous, what is the purpose of Task.Delay ? next, let's really delay the running thread by using async and await

public static void Main(string[] args){
     TaskTest();
     System.Console.WriteLine("main thread is not blocked");
     Console.ReadLine();
}
private static async void TaskTest(){
     await Task.Delay(5000);
     System.Console.WriteLine("task done");
}

async tell caller, I am an asynchronous method, don't wait for me. await inside the TaskTest() ask for waiting for the asynchronous task. Now, after running, program will wait 5 seconds to show the task done text.

Cancel a Task

Since Task is a state machine, there must be a way to cancel the task while task is in running.

static CancellationTokenSource tokenSource = new CancellationTokenSource();
public static void Main(string[] args){
    TaskTest();
    System.Console.WriteLine("main thread is not blocked");
    var input=Console.ReadLine();
    if(input=="stop"){
          tokenSource.Cancel();
          System.Console.WriteLine("task stopped");
     }
     Console.ReadLine();
}
private static async void TaskTest(){
     try{
          await Task.Delay(5000,tokenSource.Token);
     }catch(TaskCanceledException e){
          //cancel task will throw out a exception, just catch it, do nothing.
     }
     System.Console.WriteLine("task done");
}

Now, when the program is in running, you can input "stop" to cancel the Delay task.

Solution 3 - C#

Your tasks never finish because they never start running.

I would Task.Factory.StartNew to create a task and start it.

public static async Task Task1()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(5)));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Factory.StartNew(() => Thread.Sleep(TimeSpan.FromSeconds(10)));
  Debug.WriteLine("Finished Task2");
}

As a side note, if you're really just trying to pause in a async method, there's no need to block an entire thread, just use Task.Delay

public static async Task Task1()
{
  await Task.Delay(TimeSpan.FromSeconds(5));
  Debug.WriteLine("Finished Task1");
}

public static async Task Task2()
{
  await Task.Delay(TimeSpan.FromSeconds(10));
  Debug.WriteLine("Finished Task2");
}

Solution 4 - C#

Async and await are markers which mark code positions from where control should resume after a task (thread) completes. Here's a detail youtube video which explains the concept in a demonstrative manner http://www.youtube.com/watch?v=V2sMXJnDEjM

If you want you can also read this coodeproject article which explains the same in a more visual manner. http://www.codeproject.com/Articles/599756/Five-Great-NET-Framework-4-5-Features#Feature1:-“Async”and“Await”(Codemarkers)

Solution 5 - C#

static void Main(string[] args)
{
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "Main";
    Console.WriteLine(Thread.CurrentThread.Name + "1");

    TaskTest();

    Console.WriteLine(Thread.CurrentThread.Name + "2");
    Console.ReadLine();
}


private async static void TaskTest()
{
    Console.WriteLine(Thread.CurrentThread.Name + "3");

    await Task.Delay(2000);
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "FirstTask";
    Console.WriteLine(Thread.CurrentThread.Name + "4");

    await Task.Delay(2000);
    if (Thread.CurrentThread.Name == null)
        Thread.CurrentThread.Name = "SecondTask";
    Console.WriteLine(Thread.CurrentThread.Name + "5");
}

If you run this program you will see that await will use different thread. Output:

Main1
Main3
Main2
FirstTask4 // 2 seconds delay
SecondTask5 // 4 seconds delay

But if we remove both await keywords, you will learn that async alone doesn't do much. Output:

Main1
Main3
Main4
Main5
Main2

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlex MarshallView Question on Stackoverflow
Solution 1 - C#Stephen ClearyView Answer on Stackoverflow
Solution 2 - C#Andrew ZhuView Answer on Stackoverflow
Solution 3 - C#MerickOWAView Answer on Stackoverflow
Solution 4 - C#Shivprasad KoiralaView Answer on Stackoverflow
Solution 5 - C#milosView Answer on Stackoverflow