C# - Concurrency

Created:2019-03-02  Last modified:2019-03-02


  1. Introduction

    C# support language level concurrency (async/await) and library level concurrency (Task Parallel Library)

  2. Task-based Asynchronous Pattern

    C# defines a type called Task, it represents a currently running task. Using "await" keyword to yield a task and await on it.

    1. Execution order

      For example, foo function execute synchronously until hit the "await" statement. When it hit the await statement, the function immediate yield a Task<int> object to the main function. The main function goes to do other jobs. When the await statement is done, foo() function continue execute synchronously.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public static void Main() {
          Task<int> t = foo(); // 1 call, 5 yield
          t.Wait(); //6 wait, 10 finish
          Console.WriteLine("Main: after test()");
       
      }
      static async Task<int> foo() {
          HttpClient client = new HttpClient(); // 2
          Console.WriteLine("Doo: before http request"); // 3
          String text = await client.GetStringAsync("http://www.sousys.com"); // 4 yeild, 7 done
          Console.WriteLine("Doo: after http request"); // 8
          return text.Length; // 9
      }
    2. Rules

      1). "async" function can only return void, Task, Task<T>.
      2). "await" and "async "keyword must be used together.
      3). A Task object can be created by calling another async function or Task.Run method. (Task.Run is used to run CPU-bound task)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      static async void foo() {
          await Task.Run(() => 1 + 1);
      }
      static async Task foo() {
          await Task.Run(() => 1 + 1);
      }
      static async Task<int> foo() {
          await Task.Run(() => 1 + 1);
          return 10;
      }
      4). .Wait() method awaits a task in synchronous manner; await keyword awaits a task in asyncrhonous manner.
      1
      2
      3
      4
      5
      static int foo() {
          int x = 0;
          Task.Run(() => x = 1 + 1).Wait();
          return x;
      }
      5). CPU-bound task using Task.Run is running in background thread;
      Underneath, IO-bound async task is handled by the runtime with the OS (in the same thread).

    3. Using .ContinueWith to get async .Wait() result

      .ContinueWith return a new task, so invoking .Wait() on this new task.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      public static void Main()
      {
          int v = 0;
          Task<int> t = foo(); // 1 enter , 5 yeild
          Task t2 = t.ContinueWith( // 6
              (r) => {            // 11
                  Console.WriteLine(r.Result);
                  v = r.Result;
                  });
          t2.Wait(); // 7 wait , 12 done
          Console.WriteLine("Main: after test()" + v); // 13
       
      }
      static async Task<int> foo() {
          HttpClient client = new HttpClient(); // 2
          Console.WriteLine("Doo: before http request"); // 3
          String text = await client.GetStringAsync("http://www.sousys.com"); // 4 yeild, 8 done
          Console.WriteLine("Doo: after http request"); // 9
          return text.Length; // 10
      }
    4. Promise Model

  3. Task Parallel Library (TPL)

  4. References