Use and principles of async/Await

await/async is.NetFramework 4.5, grammatical sugar, functionality provided by the compiler!

await/async is a C#reserved keyword, usually paired, and the general recommendation is either not to be used or to the end

  1. The async modifier, which can appear alone, is meaningless and has warnings
  2. Await is in the method body and can only appear before task/async methods, only await will error

Here's a code to dissect the usage of async and await.

One: Only one async has no await

 1  /// <summary>
 2  /// only async No, await,There will be one warn
 3  /// No difference from the normal method
 4  /// </summary>
 5  private static async void NoReturnNoAwait()
 6  {
 7      //Main thread execution
 8      Console.WriteLine($"NoReturnNoAwait Sleep before Task,ThreadId={Thread.CurrentThread.ManagedThreadId}");
 9      Task task = Task.Run(() =>//Start a new thread to complete the task
10      {
11          Console.WriteLine($"NoReturnNoAwait Sleep before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
12          Thread.Sleep(3000);
13          Console.WriteLine($"NoReturnNoAwait Sleep after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
14      });
15 
16      //Main thread execution
17      Console.WriteLine($"NoReturnNoAwait Sleep after Task,ThreadId={Thread.CurrentThread.ManagedThreadId}");
18  }
View Code

The call is as follows:

1 private async static Task Test()
2 {
3     Console.WriteLine($"start Current Main Thread id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
4     {
5         NoReturnNoAwait();
6     }            
7     Console.WriteLine($"end Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
8     Console.Read();
9 }
View Code

The results are as follows:

If async is not used, this is equivalent to a normal method, and note that the return value of the method is received using Task, so it does not need to return directly

Two: there are async and await methods

 1  /// <summary>
 2  /// async/await 
 3  /// Not alone await
 4  /// await Can only be placed in task Front
 5  /// Not recommended void Return value, using Task To replace
 6  /// Task and Task<T>Ability to use await, Task.WhenAny, Task.WhenAll And so on. Async Void No way
 7  /// </summary>
 8  private static async Task NoReturn()
 9  {
10      //Main thread execution
11      Console.WriteLine($"NoReturn Sleep before await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
12      TaskFactory taskFactory = new TaskFactory();
13      Task task = taskFactory.StartNew(() =>
14      {
15          Console.WriteLine($"NoReturn Sleep before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
16          Thread.Sleep(6000);
17          Console.WriteLine($"NoReturn Sleep after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
18      });  
19              
20      await task;//The main thread returns here to perform the main thread task
21      Console.WriteLine($"NoReturn Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
22 
23      //Notice above
24      // await task;
25      // Console.WriteLine($"NoReturn Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
26      //Equivalent to the following code
27      //task.ContinueWith(t =>
28      //{
29      //    Console.WriteLine($"NoReturn Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
30      //});
31  }
View Code

The call is as follows:

 1 private async static Task Test()
 2 {
 3     Console.WriteLine($"start Current Main Thread id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
 4     {
 5         NoReturn();
 6         for (int i = 0; i < 5; i++)
 7         {
 8             Thread.Sleep(3000);
 9             Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId.ToString("00")} i={i}");
10         }
11     }
12     Console.WriteLine($"end Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
13     Console.Read();
14 }
View Code

The results are as follows:

As you can see from the above results:

1: The main thread calls the async/await method, and the main thread encounters await and returns to perform subsequent actions.
2:The code behind await waits for the task to finish before continuing, which is like wrapping the code behind await into a callback action for continue
3: The callback action may then be a Task thread, a new child thread, or a main thread

3: Tasks with return values must have t.Result if they are to be returned

 1 /// <summary>
 2 /// With return value Task  
 3 /// To use the return value, you must wait for the subthread to finish calculating
 4 /// </summary>
 5 /// <returns>async Only return long</returns>
 6 private static async Task<long> SumAsync()
 7 {
 8     Console.WriteLine($"SumAsync 111 start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
 9     long result = 0;
10 
11     await Task.Run(() =>
12     {
13         for (int k = 0; k <3; k++)
14         {
15             Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
16             Thread.Sleep(1000);
17         }
18         for (long i = 0; i < 999999999; i++)
19         {
20             result += i;
21         }
22     });
23 
24     Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
25     await Task.Run(() =>
26     {
27         for (int k = 0; k <3; k++)
28         {
29             Console.WriteLine($"SumAsync11111 {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
30             Thread.Sleep(1000);
31         }
32         for (long i = 0; i < 999999999; i++)
33         {
34             result += i;
35         }
36     });          
37     Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
38 
39     return result;
40 }
View Code

The call is as follows:

 1 private async static Task Test()
 2 {
 3     Console.WriteLine($"start Current Main Thread id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
 4     {
 5         Task<long> t = SumAsync();
 6         Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
 7         long result = t.Result;//Visit result   Main Thread Waiting Task Completion is equivalent to t.Wait();
 8       
 9 
10         Console.WriteLine($"result=={result}");
11     }
12     Console.WriteLine($"end Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
13     Console.Read();
14 }
View Code

The results are as follows:

You can see from the above that using t.Result or t.Wait() is equivalent to blocking the main thread, that is, waiting for the child thread to finish before the main thread can do the following, while await is not blocking the main thread

 

Four: Here's a code and decompilation look at how the bottom of async works

 1 public class AwaitAsyncILSpy
 2     {
 3         public static void Show()
 4         {
 5             Console.WriteLine($"start1 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
 6             Async();
 7             Console.WriteLine($"aaa2 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
 8         }
 9         private static async void Async()
10         {
11             Console.WriteLine($"ddd5 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
12             await Task.Run(() =>
13             {
14                 Thread.Sleep(500);
15                 Console.WriteLine($"bbb3 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
16             });
17             Console.WriteLine($"ccc4 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
18         }
19     }
View Code

The results from the above analysis are: 15234

We use ILSPy to decompile the dll and get the following image:

By adding async to the above figure discovery method, the AsyncStateMachine state (which implements the IAsyncStateMachine interface) is added at decompilation time, and it works by:

Initialization state-1--Execution modifies state 0--Execution modifies state-1--Execution modifies state 0--If other states (-2) end, just like a traffic light, there is no limit to the cycle traffic light-green light-traffic light-green light

The underlying implementation is as follows:

Posted by TronB24 on Thu, 23 Apr 2020 10:52:15 -0700