In the previous article, we discussed why asynchronous programming is needed from the creation of responsive interfaces and the construction of highly scalable service applications. What benefits does asynchronous programming bring to us. Let's get started with the benefits, but in asynchronous programming, it is always easier than doing it. It is not a famous saying that it is difficult to write asynchronous programs, especially reliable asynchronous programs. Because asynchronous programs are very difficult to write and prone to errors, many basic constructor elements cannot be used in asynchronous programming, which makes our developers more willing to write Synchronous Code, although we know that Asynchronization should be used in some places.
How to Implement Asynchronization
For many people, Asynchronization is to use background threads to run time-consuming operations. Sometimes this is true, but not in most of our daily scenarios.
For example, we have the following requirement: Use HttpWebRequest to request the content of a specified URI and then output it to the text domain on the interface. The synchronization code is easy to write:
1: private void btnDownload_Click (object sender, EventArgs e)
2 :{
3: var request = HttpWebRequest. Create ("http://www.sina.com.cn ");
4: var response = request. GetResponse ();
5: var stream = response. GetResponseStream ();
6: using (StreamReader reader = new StreamReader (stream ))
7 :{
8: var content = reader. ReadToEnd ();
9: this.txt Content. Text = content;
10 :}
11 :}
Right, very simple. But as mentioned in the previous article, the short program experience will be very poor. Especially when the URI points to a very large resource and the network is very slow, the interface will be suspended when you click the Download button to obtain the result.
Oh, now you think of Asynchronization. Recall the previous article. We found that as long as we put time-consuming operations on another thread for execution, our UI thread can continue to respond to user operations.
Implement Asynchronization using independent threads
If you write the following code:
1: private void btnDownload_Click (object sender, EventArgs e)
2 :{
3: var downloadThread = new Thread (Download );
4: downloadThread. Start ();
5 :}
6:
7: private void Download ()
8 :{
9: var request = HttpWebRequest. Create ("http://www.sina.com.cn ");
10: var response = request. GetResponse ();
11: var stream = response. GetResponseStream ();
12: using (StreamReader reader = new StreamReader (stream ))
13 :{
14: var content = reader. ReadToEnd ();
15: this.txt Content. Text = content;
16 :}
17 :}
Then, F5 runs. Unfortunately, an exception occurs here: we cannot update the UI attributes on a non-UI thread (for more details, refer to my article: WinForm two three things (3) Control. invoke & Control. beginInvoke ). We temporarily ignore this exception (it will not appear in release mode, but this is not recommended ).
Oh, after you write the above Code, you will find that the UI is no longer blocked. I thought, asynchronous is just like this. After a while, you suddenly remembered in which book did you see that you should try not to declare the Thread yourself, and the application uses the Thread pool. If you search for MSDN, change the above Code to the following:
1: private void btnDownload_Click (object sender, EventArgs e)
2 :{
3: ThreadPool. QueueUserWorkItem (state) =>{ Download ();});
4 :}
5:
6: private void Download ()
7 :{
8: var request = HttpWebRequest. Create ("http://www.sina.com.cn ");
9: var response = request. GetResponse ();
10: var stream = response. GetResponseStream ();
11: using (StreamReader reader = new StreamReader (stream ))
12 :{
13: var content = reader. ReadToEnd ();
14: this.txt Content. Text = content;
15 :}
16 :}
Well, it's easy to complete. You admire yourself a little. In such a short period of time, even the "advanced technology" of the thread pool is used. When you are complacent, one of your colleagues came over and said:This implementation method is very inefficient. The time-consuming operations here are IO operations, not computing-intensive, and can be left with no threads assigned to it.(Although not accurate, I think so if I do not study it in depth ).
BeginInvoke & EndInvoke
In. NET, the delegate also provides two other methods: BeginInvoke and EndInvoke to implement Asynchronization. In fact, this implementation method is similar to using a thread to implement it, and will occupy CPU time. If I/O operations are encountered, the thread will still be blocked.
So when we need to asynchronous time-consuming operations, we must clearly identify the type of time-consuming operations.
Your colleagues are right. For IO operations (such as read/write disks, network transmission, and database queries), we do not need to use a thread for execution. Modern disks and other devices can work with the CPU at the same time. During this period of time, the CPU can do other things. After reading the data, the CPU can be involved after interruption. Therefore, although the above Code has built a responsive interface, it has created a thread that does nothing (this thread will be blocked during the time of network requests ). Therefore, if you want to implement Asynchronization, consider whether time-consuming operations are computing-intensive or IO-intensive. Different operations require different policies. For Computing-intensive operations, you can use the above method: for example, you need to solve complicated equations. Whether to use a dedicated thread or a thread pool depends on the degree to which your operations are critical.
At this time, you are thinking again, not letting me use threads, but also letting me implement Asynchronization. What should I do? Microsoft has helped you think of this for a long time. in the. NET Framework, almost all IO operations are provided with the synchronous and asynchronous versions. Microsoft also defines two asynchronous programming modes to simplify asynchronous usage:
Classic Async Pattern
This method provides two methods for asynchronous programming: for example, the Read method of System. IO. Stream:
Public int Read (byte [] buffer, int offset, int count );
It also provides two methods for asynchronous reading:
Public IAsyncResult BeginRead (byte [] buffer, int offset, int count, AsyncCallback callback );
Public int EndRead (IAsyncResult asyncResult );
The method that starts with in initiates an asynchronous operation. The method that starts with Begin also receives an AsyncCallback type callback, which is executed after the asynchronous operation is completed. Then, we can call EndRead to obtain the asynchronous operation result. I will not elaborate more on the details of this mode. If you are interested, read chapter 26 and chapter 27 of CLR via C # And 《. the description of the asynchronous mode in the. NET design specification. Here I will use this mode to re-implement the above code snippet:
1: private static readonly int BUFFER_LENGTH = 1024;
2:
3: private void btnDownload_Click (object sender, EventArgs e)
4 :{
5: var request = HttpWebRequest. Create ("http://www.sina.com.cn ");
6: request. BeginGetResponse (ar) => {
7: var response = request. EndRequest (ar );
8: var stream = response. GetResponseStream ();
9: ReadHelper (stream, 0 );
10 :}, null );
11 :}
12:
13: private void ReadHelper (Stream stream)
14 :{
15: var buffer = new byte [BUFFER_LENGTH];
16: stream. BeginRead (buffer, 0, BUFFE