C#-Async keyword (asynchronous method)

async keyword (asynchronous method)

The async keyword is unique to C#. Java has no such thing.
async is a contextual keyword in the C# world. It is automatically recognized as a keyword by the compiler only when it modifies a method, and can be used for any other purpose such as variable names in other places in the code.
The async keyword is used to modify two types of methods: lambda expressions or asynchronous methods.
A method with async modification is called async method, such as:

public async Task<int> ExampleMethodAsync()  
{
    
      
     // (1) To do some code here synchronously...
    await .....//  (2) To do something asynchronously....
    // (3) To do some code here after awiat code...
} 

Just like the above method ExampleMethodAsync(), Grandpa Microsoft likes to add an Async suffix after defining the name of an asynchronous function (this is not necessary, adding or not adding a compiler will neither report an error nor affect the asynchronous feature), tell us This method is an asynchronous method. When we define asynchronous methods ourselves, we can also copy this Microsoft habit. Inside the
async modified method, there should be an await keyword, and the two keywords generally appear in pairs. Of course, if we accidentally forget to write await expressions or statements, this async method runs synchronously by default. At the same time, the compiler will kindly prompt us whether we missed await . In addition, inside the async method, there can be multiple await statements. The statements run by
awiat are generally time-consuming tasks (that is, some operations that will block the main thread, such as obtaining Http responses, writing documents, saving databases, etc.), otherwise there is no need for asynchrony.
Take the above example as an example (assuming that the await in the example is the first await ), the asynchronous method execution process (a rough look):

  1. After the main thread enters the method ExampleMethodAsync(), it executes (1) first; if there is a statement to create Task or Task<TResult> in (1), or call other async methods (the return value is Task or Task<TResult>) For the convenience of description, we all call it Task creation statement; for example, create a Task or Task<TResult> directly:
var tsk = Task.Run(()=>{
    
    
	Thread.Sleep(1000);
	Console.Writeline("Do another job asynchronously.");
});

Or call another async method:

Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com/dotnet");

Then, the asynchronous task has already started running when the Task creation statement is called (the statement call itself is in the main thread, and the internal task is executed in a new thread), that is, the asynchronous thread has been started at this time , Because it is started asynchronously, it does not prevent the main thread from continuing to go down;

  1. Next, the main thread will run sequentially to the first await inside the async method. If the first await calls an async method, then the main program continues to enter the method execution until it encounters an await task. The thread will jump out of the ExampleMethodAsync method; for example:
	static void  Main(string[] args)
	{
    
    
		// do something...
		ExampleMethodAsync();
		// do someting else...
	}
    public static async void ExampleMethodAsync()
    {
    
    
        // (1) 执行一些任务Do2Async()前准备的事情...
        await Do2Async(); // (2)
        // (3) 运行一些Do2Async()执行完之后的事情...
    }
	public static Task Do2Async()
	{
    
    
	    // 执行一些t任务执行前的事情,比如任务的准备...
	    Task t = Task.Run(()=>{
    
    
	    // 异步任务中执行费时的事情...
	    });
	    // 运行一些与t无关的事情...
	    await t;
	    // 在这里执行一些t任务执行完相关的事情...
	}

The caller (that is, the main thread where main is located) will execute until line 20 before jumping out of the ExampleMethodAsync() method, not at line 10.

  1. The remaining (3) in the ExampleMethodAsync() method is executed after the await(2) part is executed.
  2. Suppose there is a second, third...awiat in ExampleMethodAsync(), because the main program has already jumped out, the subsequent await will be executed in order in the asynchronous thread.

The async method can have the following three return types:

  • Task
  • Task<TResult>
  • The void return type is generally used in event handlers, or in situations where you only need task execution and do not care about the results of task execution.
  • Any other type with GetAwaiter method (since C#7.0)

Note that we cannot wait (awiat) for an async void method.

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

namespace test
{
    
    
    class Program
    {
    
    
        static void Main(string[] args)
        {
    
    
            Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  Hello, I am Caller!");
            DoAsync();
            Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  Hello, I am Caller too!");
            Console.Read();
        }
        public static async void DoAsync()
        {
    
    
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  In DoAsync(), before SunAsync()");
            await SunAsync();
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  After SunAsync(), DoAsync() End.");
        }
        public static async Task SunAsync()
        {
    
    
            var t = Task.Run(()=>{
    
    
                    System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  New Task~");
                    for(int i=0 ; i<10; i++)
                    {
    
    
                        Thread.Sleep(1000);
                        System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  I am playing game...");                    
                    }
                });
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  After Task, before await.");
            await t;
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  After await, before SunAsync() exit.");
        }
    }
}

The result at this time:

ThreadID:1  Hello, I am Caller!
ThreadID:1  In DoAsync(), before SunAsync()
ThreadID:1  After Task, before await.
ThreadID:4  New Task~
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:4  I am playing game...
ThreadID:1  After await, before SunAsync() exit.
ThreadID:1  After SunAsync(), DoAsync() End.
ThreadID:1  Hello, I am Caller too!
Read this code and results carefully, and understand carefully, this code is an ansync void method embedded in an ansync Task method. Pay attention to the experience, it does not mean that the DoAsync() method is immediately exited as soon as it encounters the await main program (the caller of the ansync method), but the execution reaches line 33, and it jumps out when it encounters the first Task. From the output ThreadID number of this example, it can be seen that the content after the 33 line await is run in a new thread (4 threads). The content before line 33 await runs in the main thread (1 thread).

If you change the SunAsync() code to (add a Thread.Sleep(150000) before await):


  public static async Task SunAsync()
        {
    
    
            var t = Task.Run(()=>{
    
    
                    System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  New Task~");
                    for(int i=0 ; i<10; i++)
                    {
    
    
                        Thread.Sleep(1000);
                        System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  I am playing game...");                    
                    }
                });
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  After Task, before await.");
            Thread.Sleep(15000); //主线程睡15秒
            await t;
            System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId}  After await, before SunAsync() exit.");
        }

ThreadID:1 Hello, I am Caller!
ThreadID:1 In DoAsync(), before SunAsync()
ThreadID:1 After Task, before await.
ThreadID:4 New Task~
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:1 After await, before SunAsync() exit.
ThreadID:1 After SunAsync(), DoAsync() End.
ThreadID:1 Hello, I am Caller too!

Because the task of Task.Run() ends before it runs to await, therefore, the content after await is still executed in the main thread (1 thread). This example tells us that if the task has been executed before the await, the content after the await will still be executed in the original thread.

In short, the async method caller exits the async method body when it encounters an actual await task. Generally, things that have nothing to do with asynchronous tasks are processed before await (this part of the code is executed by the thread where the caller of the asynchronous method is located), and the code after await deals with things after the asynchronous task is processed, so this part of the code can be processed Things related to asynchronous tasks (this part is generally executed in a newly created asynchronous thread, unless the task has been executed quickly before calling await, then this part may still be executed in the caller thread) .

Guess you like

Origin blog.csdn.net/MrLsss/article/details/106895685