Considering that the thinking of directly implementing a Task library is a bit jumpy, this section mainly explains the essential role of Async/Await (what problem has been solved) and the working principle of Async/Await. The library that implements a Task class will be discussed later. First, let's reviewPrevious blogscene.
class Program { public static string GetMessage() { return (); } public static string TranslateMessage(string msg) return msg; public static void DispatherMessage(string msg) switch (msg) { case "MOUSE_MOVE": { OnMOUSE_MOVE(msg); break; } case "MOUSE_DOWN": OnMouse_DOWN(msg); default: break; } public static void OnMOUSE_MOVE(string msg) ("Start drawing mouse shape"); public static int Http() (1000);//Simulate network IO delay return 1; public static void HttpAsync(Action<int> action,Action error) // Here we use another thread to implement asynchronous IO. Since the Http method simulates network IO delay through Sleep, we can only implement asynchronous IO through another thread here. //But remember that multithreading is just a means to implement asynchronous IO, it is not necessary. Later, we will talk about how to implement asynchronous IO through a thread. Thread thread = new Thread(() => try { int res = Http(); action(res); } catch error(); }); (); public static Task<int> HttpAsync() return (() => return Http(); public static void OnMouse_DOWN(string msg) HttpAsync() .ContinueWith(t => if( == ) }else if( == ) (1); //Do some work }) if ( == ) else if ( == ) (2); (3); }); static void Main(string[] args) while (true) string msg = GetMessage(); if (msg == "quit") return; string m = TranslateMessage(msg); DispatherMessage(m); }
In the OnMouse_DOWN processing function, we use Task's ContinueWith function for chain operations, which solved the callback hell problem, but we always feel a little uncomfortable. We imagine that there is a keyword await that can achieve the following functions: First of all, await must be of Task type, and must be of Task type (actually not a necessary condition, as will be discussed later). The reason is to ensure that the ContinueWith function must be. If the Task does not return value, put the code behind await into the ContinueWith function in the Task. If there is a return value, the result after Await is converted into access attributes. The text may not be understood. See the example code.
//Before no return value conversionpublic async void Example() { Task t = (() => { (1000); }); await t; //Do some work} //No return value after conversionpublic void Example() (task => //Do some work //Before the return value is converted Task<int> t = <int>(() => return 1; int res = await t; // Use res to do some work//After the return value is converted //Use to do some work
It looks good, but at least there are the following problems, as follows:
- This conversion method cannot convert the Try/Catch structure very well
- Using await in loop structure is not easy to convert
- This implementation is closely related to the Task type
One or two points are what I think, but the third point can be proved from the point of extending async/await. But no matter what, async/await just transforms the method according to certain rules, and it has nothing special. Specifically, it is to put the code to be executed after Await into a function similar to ContinueWith. In C#, it is expressed in the form of a state machine. Each state corresponds to a part of the code. The state machine has a MoveNext() method. MoveNext() executes different codes according to different states. Then the code corresponding to each state part will set the next state field, and then put its own MoveNext() method into a function similar to ContinueWith() to execute. The entire state machine is driven by the callback function. We try to manually convert the following async/await method.
public static Task WorkAsync() { return (() => { (1000); ("Done!"); }); } public static async void Test() { ("Step 1"); await WorkAsync(); ("Step 2"); await WorkAsync(); ("Step 3"); }
Manually write a simple state machine class
public class TestAsyncStateMachine { public int _state = 0; public void Start() => MoveNext(); public void MoveNext() { switch(_state) { case 0: { goto Step0; } case 1: goto Step1; default: ("Step 3"); return; } Step0: ("Step 1"); _state = 1; WorkAsync().ContinueWith(t => ()); return; Step1: _state = -1; ("Step 2"); } }
And the Test() method becomes like this
public static void Test() { new TestAsyncStateMachine().Start(); }
Note that the Test() method returns void, which means that the caller will not be able to await Test(). If the Task is returned, this state machine class cannot be handled correctly. If it is handled correctly, the state machine must return a Task after the start() is started. This Task must become a complete state after the entire state machine is flowed, so that the ContinueWith called by the caller on the Task can be continued to be executed. As for the Task class, it does not provide this method (with internal but not exposed to the outside) to actively control the state of the Task. This is different from the Promise in JS. The Reslove function is used in JS to actively control the state of the Promise, and the then chain call on the Promise can continue to be completed. How to do it in C#? Since a state machine is used to implement async/await, you will definitely encounter it when converting a function that returns a Task. How to deal with it? Let's talk about it later.
First, solve the problem of closely related to the Task type.
From the state machine, we can see that the ContinueWith function in Task is mainly used. Its semantics is to execute the callback function after the task is completed and get the result through the callback function. This programming style is also called CPS (Continuation-Passing-Style, Continuation Transfer Style). So can we abstract this function? Of course, language developers thought that it was abstracted into an Awaiter. Therefore, the compiler requires that the type of await must have a GetAwaiter method. What type is Awaiter? The compiler stipulates that the following methods are mainly implemented, which is Awaiter:
- The INotifyCompletion interface must be inherited and the OnCompleted(Action continue) method must be implemented.
- Must include the IsCompleted property
- Must include GetResult() method
The first point is easy to understand, the second point is thermal path optimization, and the third point is discussed later. Let's remodel the state machine we wrote manually.
public class TestAsyncStateMachine { public int _state = 0; public void Start() => MoveNext(); public void MoveNext() { switch(_state) { case 0: { goto Step0; } case 1: goto Step1; default: ("Step 3"); return; } Step0: ("Step 1"); _state = 1; TaskAwaiter taskAwaiter; taskAwaiter = WorkAsync().GetAwaiter(); if () goto Step1; (() => ()); return; Step1: _state = -1; ("Step 2"); if () MoveNext(); } }
You can see that the coupling relationship with ContinueWith in Task has been removed, and if the task has been completed, the next task can be directly executed, avoiding useless overhead.
Therefore, we can summarize async/await:
- async/await only means that this method requires special processing by the compiler, and does not mean that it must be asynchronous.
- GetAwaiter in the Task class is mainly used by the compiler.
First point we can use the following examples to prove that interested friends can verify the following by themselves in order to deepen their understanding.
//This type contains the GetAwaiter method, and the type returned by GetAwaiter() contains three necessary conditionspublic class MyAwaiter : INotifyCompletion { public void OnCompleted(Action continuation) { continuation(); } public bool IsCompleted { get; } public void GetResult() public MyAwaiter GetAwaiter() => new MyAwaiter(); }
A test function, note that it must return void
public static async void AwaiterTest() { await new MyAwaiter(); ("Done"); }
You can see that this is done in full synchronization.
This is the article about the role of Async/Await from shallow to deep (2). This is the end of this article. For more related content about Async/Await, please search for my previous articles or continue browsing the following related articles. I hope you can support me in the future!