[Switch] C # asynchronous world [Top ],
Reading directory
- APM
- EAP
- TAP
- Extended thinking
The new advanced programmers may have used a lot of async and await, but have little knowledge about the previous Asynchronization. I plan to review the historical asynchronous process.
This article will focus on the async asynchronous mode.
APM
APM Asynchronous Programming Model, Asynchronous Programming Model
APM was available as early as C #1. Although not very familiar, I still have seen it. That is, the BeginXXX and EndXXX classes, and the BeginXXX return value is the IAsyncResult interface.
Before formally writing an APM example, we will first provide a synchronization code:
// 1. Synchronization Method private void button#click (object sender, EventArgs e) {Debug. writeLine ("[Debug] Thread ID:" + Thread. currentThread. managedThreadId); var request = WebRequest. create ("https://github.com/"); // to better demonstrate the effect, we use a slow Internet request. getResponse (); // send the Debug request. writeLine ("[Debug] Thread ID:" + Thread. currentThread. managedThreadId); label1.Text = "finished! ";}
[Note] to better demonstrate the asynchronous effect, we use the winform program as an example. (Because winform always requires UI thread rendering interface, if it is occupied by UI threads, it will be in the "false" status)
【]
The figure shows that:
- When we run the method, the page displays "false", which does not move.
- We can see the printed result. The thread ids before and after the method call are both 9 (that is, the same thread)
Next we will demonstrate the corresponding Asynchronous Method: (BeginGetResponse, EndGetResponse so-called APM asynchronous model)
Private void button2_Click (object sender, EventArgs e) {// 1. APM asynchronous programming model, asynchronous Programming Model // C #1 [implements BeginXXX and EndXXX Based on the IAsyncResult interface] Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId); var request = WebRequest. create (" https://github.com/ "); Request. beginGetResponse (new AsyncCallback (t => // callback after execution is complete {var response = request. endGetResponse (t); var stream = response. getResponseStream (); // get the returned data stream using (StreamReader reader = new StreamReader (stream) {StringBuilder sb = new StringBuilder (); while (! Reader. endOfStream) {var content = reader. readLine (); sb. append (content);} Debug. writeLine ("[Debug]" + sb. toString (). trim (). substring (0,100) + "... "); // only take the first 100 characters of the returned content for Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId); label1.Invoke (Action) () => {label1.Text = "finished! ";})); // Here cross-thread access to the UI requires processing}), null); Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId );}
【]
The figure shows that:
- Enabling the asynchronous method is not a UI interface dead
- The Asynchronous Method starts another thread with ID 12.
The code execution sequence is as follows:
As we mentioned earlier, APM BebinXXX must return the IAsyncResult interface. Next we will analyze the IAsyncResult interface:
First, let's look:
The IAsyncResult interface is returned. What does IAsyncResult look like? :
It is not as complicated as you think. Can we try to implement this interface and display our Asynchronous Method?
First define a class of MyWebRequest, And Then inherit IAsyncResult :( below is the basic pseudo-code implementation)
public class MyWebRequest : IAsyncResult{ public object AsyncState { get { throw new NotImplementedException(); } } public WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { throw new NotImplementedException(); } } public bool IsCompleted { get { throw new NotImplementedException(); } }}
This is definitely not usable. At least there must be an attribute of the stored callback function. Below we will slightly modify it:
Then we can customize the APM asynchronous model: (paired in and End)
Public IAsyncResult MyBeginXX (AsyncCallback callback) {var asyncResult = new MyWebRequest (callback, null); var request = WebRequest. create ("https://github.com/"); new Thread () => // re-enable a Thread {using (StreamReader sr = new StreamReader (request. getResponse (). getResponseStream () {var str = sr. readToEnd (); asyncResult. setComplete (str); // set asynchronous result }}). start (); return asyncResult; // return an IAsyncResult} public string MyEndXX (IAsyncResult asyncResult) {MyWebRequest result = asyncResult as MyWebRequest; return result. result ;}
The call is as follows:
Private void button4_Click (object sender, EventArgs e) {Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId); MyBeginXX (new AsyncCallback (t => {var result = MyEndXX (t); Debug. writeLine ("[Debug]" + result. trim (). substring (0,100) + "... "); Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId) ;})); Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId );}
:
We can see that our implementation is basically the same as what the system provides.
- Enabling the asynchronous method is not a UI interface dead
- The Asynchronous Method starts another thread with ID 11.
[Summary]
I personally think that the asynchronous mode of APM is to enable another thread to execute time-consuming tasks and then perform subsequent operations through the callback function.
APM can also obtain values in other ways, such:
While (! AsyncResult. IsCompleted) // loop until asynchronous execution is completed (polling mode) {Thread. Sleep (100);} var stream2 = request. EndGetResponse (asyncResult). GetResponseStream ();
Or
AsyncResult. AsyncWaitHandle. WaitOne (); // stop the thread until the asynchronous completion (blocking wait) var stream2 = request. EndGetResponse (asyncResult). GetResponseStream ();
Supplement: if it is a common method, we can also use the delegate asynchronous: (BeginInvoke, EndInvoke)
Public void MyAction () {var func = new Func <string, string> (t => {Thread. sleep (2000); return "name:" + t + DateTime. now. toString () ;}); var asyncResult = func. beginInvoke ("Zhang San", t => {string str = func. endInvoke (t); Debug. writeLine (str) ;}, null );}
EAP
EAP Event-based Asynchronous Pattern
This mode follows C #2.
Let's look at an EAP example:
Private void button3_Click (object sender, EventArgs e) {Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId); BackgroundWorker worker = new BackgroundWorker (); worker. doWork + = new DoWorkEventHandler (s1, s2) => {Thread. sleep (2000); Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId) ;}); // register an event to Implement Asynchronous worker. runWorkerAsync (this); Debug. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId );}
[] (Also does not block the UI Interface)
[Features]
- Register callback functions using events
- Execute asynchronous call using XXXAsync
The example is simple, but it is not as clear and transparent as compared with the APM mode. Why is this possible? What is event registration? Why does RunWorkerAsync trigger the registered function?
I feel like I think more...
Let's try to decompile the source code:
I just want to say, is this fun?
TAP
Task-based Asynchronous Pattern
So far, do we think the above APM and EAP asynchronous modes are easy to use? It seems that no problems have been found. Think about it again... if we have multiple asynchronous methods that need to be executed sequentially, and all the return values must be obtained (in the main process.
First, define three delegates:
public Func<string, string> func1(){ return new Func<string, string>(t => { Thread.Sleep(2000); return "name:" + t; });}public Func<string, string> func2(){ return new Func<string, string>(t => { Thread.Sleep(2000); return "age:" + t; });}public Func<string, string> func3(){ return new Func<string, string>(t => { Thread.Sleep(2000); return "sex:" + t; });}
Then execute the command in a certain order:
Public void MyAction () {string str1 = string. empty, str2 = string. empty, str3 = string. empty; IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null; asyncResult1 = func1 (). beginInvoke ("Zhang San", t => {str1 = func1 (). endInvoke (t); Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId); asyncResult2 = func2 (). beginInvoke ("26", a => {str2 = func2 (). endInvoke (a); Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId); asyncResult3 = func3 (). beginInvoke ("male", s => {str3 = func3 (). endInvoke (s); Debug. writeLine ("[Debug] asynchronous Thread ID:" + Thread. currentThread. managedThreadId) ;}, null); asyncResult1.AsyncWaitHandle. waitOne (); asyncResult2.AsyncWaitHandle. waitOne (); asyncResult3.AsyncWaitHandle. waitOne (); Debug. writeLine (str1 + str2 + str3 );}
It seems nothing except ugly and hard to read. But is that true?
Is asyncResult2 null?
It can be seen that asyncResult2 is not assigned a value before the first asynchronous operation is completed, and an exception is reported when asyncResult2 executes the asynchronous wait. In this way, we cannot control the three asynchronous functions and get the returned values after the execution is completed in a certain order. (There are other methods in theory, but the code will be more complicated)
Yes. Now our TAP is on the stage.
You only need to call the static method Run of the Task class to easily use Asynchronization.
Return Value:
Var task1 = Task <string>. run () => {Thread. sleep (1, 1500); Console. writeLine ("[Debug] task1 Thread ID:" + Thread. currentThread. managedThreadId); return "Zhang San" ;}); // other logic task1.Wait (); var value = task1.Result; // obtain the return value Console. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId );
Now we process the preceding asynchronous execution in order:
Console. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId); string str1 = string. empty, str2 = string. empty, str3 = string. empty; var task1 = Task. run () => {Thread. sleep (500); str1 = "Name: James,"; Console. writeLine ("[Debug] task1 Thread ID:" + Thread. currentThread. managedThreadId );}). continueWith (t => {Thread. sleep (500); str2 = "Age: 25,"; Console. writeLine ("[Debug] task2 Thread ID:" + Thread. currentThread. managedThreadId );}). continueWith (t => {Thread. sleep (500); str3 = "hobby: Sister"; Console. writeLine ("[Debug] task3 Thread ID:" + Thread. currentThread. managedThreadId) ;}); Thread. sleep (2500); // other logic code task1.Wait (); Debug. writeLine (str1 + str2 + str3); Console. writeLine ("[Debug] main Thread ID:" + Thread. currentThread. managedThreadId );
[]
We can see that all results are obtained and executed asynchronously in order. The logic of the code is very clear. If you feel that it is not very big, what would happen if 100 asynchronous methods need to be executed asynchronously in order? Asynchronous callback using APM requires at least 100 asynchronous callback nesting. The complexity of the code can be imagined.
Extended thinking
How WaitOne waits for completion
Why does Asynchronization improve performance?
Is there an inevitable relationship between the number of threads used and CPU usage?
Question 1: How WaitOne waits for completion
Before that, let's take a simple look at the multi-thread signal control AutoResetEvent class.
var _asyncWaitHandle = new AutoResetEvent(false);_asyncWaitHandle.WaitOne();
This code will keep waiting at WaitOne. Unless another thread executes the set Method of AutoResetEvent.
var _asyncWaitHandle = new AutoResetEvent(false);_asyncWaitHandle.Set();_asyncWaitHandle.WaitOne();
In this way, you can directly execute WaitOne. There is no waiting.
Now, do we know something about WaitOne wait in the APM asynchronous programming model. Let's go back and implement the asynchronous waiting of the custom Asynchronous Method.
Public class MyWebRequest: callback {// asynchronous callback function (delegate) private AsyncCallback _ asyncCallback; private AutoResetEvent _ callback; public MyWebRequest (AsyncCallback asyncCallback, object state) {_ asyncCallback = callback; _ asyncWaitHandle = new AutoResetEvent (false);} // set the public void SetComplete (string result) {Result = result; IsCompleted = true; _ asyncWaitHandle. set (); if (_ asyncCal Lback! = Null) {_ asyncCallback (this) ;}// return value of the asynchronous request public string Result {get; set ;}// gets the User-Defined Object, it limits or contains information about asynchronous operations. Public object AsyncState {get {throw new NotImplementedException () ;}// gets the System. Threading. WaitHandle used to wait for the asynchronous operation to complete. Public WaitHandle AsyncWaitHandle {// get {throw new NotImplementedException ();} get {return _ asyncWaitHandle;} // gets a value indicating whether the asynchronous operation is synchronized. Public bool CompletedSynchronously {get {throw new NotImplementedException () ;}// gets a value indicating whether the asynchronous operation has been completed. Public bool IsCompleted {get; private set ;}}
The red code is the newly added asynchronous wait.
[Procedure]
Question 2: Why does Asynchronization improve performance?
For example, the synchronization code:
Thread. Sleep (10000); // assume this is a method for accessing the database. Thread. Sleep (10000); // assume this is a method for accessing the FQ website.
This Code takes 20 seconds.
If it is asynchronous:
Var task = Task. run () => {Thread. sleep (10000); // suppose this is a method for accessing the database}); Thread. sleep (10000); // assume this is a method task for accessing the FQ website. wait ();
This takes 10 seconds. This saves 10 seconds.
If:
Var task = Task. Run () => {Thread. Sleep (10000); // assume this is a database access method}); task. Wait ();
There is no time-consuming code in asynchronous execution.
Or:
Var task = Task. run () => {Thread. sleep (10000); // suppose this is a method for accessing the database}); task. wait (); Thread. sleep (10000); // assume this is a method for accessing the FQ website.
After a time-consuming task is placed in an asynchronous wait, such code will not improve performance.
There is another situation:
If a single-core CPU is used for highly intensive operations, Asynchronization is meaningless. (Because the computation is very CPU-consuming, and the network request wait does not consume CPU)
Question 3: Is there an inevitable relationship between the number of threads used and the CPU usage?
Answer.
Let's make a single-core hypothesis.
Case 1:
long num = 0;while (true){ num += new Random().Next(-100,100); //Thread.Sleep(100);}
In single-core mode, we only start one thread to fill up your CPU.
Started eight times, the CPU of the eight processes is basically full.
Case 2:
More than one thousand threads, and the CPU usage is 0. As a result, we have come to the conclusion that there is no bound relationship between the number of threads used and CPU usage.
Even so, you cannot open the thread unlimitedly. Because:
- The process of starting a new thread is resource-consuming. (However, the thread pool is used to reduce the resources consumed by enabling new threads)
- Multi-thread switching also takes time.
- Each thread occupies a certain amount of memory to save the thread context information.
Demo: http://pan.baidu.com/s/1slOxgnF