Refs
Https://msdn.microsoft.com/zh-cn/magazine/ff959203.aspx
Asynchronous programming is a set of techniques for implementing a large overhead operation that runs concurrently with the rest of the program. One area where asynchronous programming often occurs is the graphical UI of the program environment: freezing the UI is usually unacceptable when expensive operations are complete. In addition, asynchronous operations are important for server applications that require concurrent processing of multiple client requests.
Typical examples of asynchronous operations that occur during practice include sending requests to the server and waiting for responses, reading data from the hard disk, and running a more expensive calculation such as a spelling checker.
Take a UI-containing application for example. The application can be built using Windows Presentation Foundation (WPF) or Windows Forms. In such an application, most of the code executes on the UI thread because it executes an event handler for the event originating from the UI control. When the user clicks a button, the UI thread picks the message and executes the Click event handler.
Now, suppose that in the Click event handler, the application sends the request to the server and waits for a response:
// !!!
Bad code!!!
void Button_Click (object sender, RoutedEventArgs e) {
WebClient client = new WebClient ();
Client. DownloadFile ("http://www.microsoft.com", "index.html");
}
There is a major problem with this code: downloading a Web site takes a few seconds or more. Next, the call to Button_Click takes a few seconds to return. This means that the UI thread will be blocked for several seconds and the UI will be frozen. Freezing the interface can lead to poor user experience, which is almost unacceptable.
For the application UI to respond at any time until the server responds, it is important to ensure that the download is not a synchronous operation on the UI thread.
Let's try to solve the freeze UI problem. One possible, but not optimal, solution is to communicate with the server on different threads so that the UI thread remains in an unblocked state. The following is an example of communicating with a server using thread pool threads:
suboptimal code
void Button_Click (object sender, RoutedEventArgs e) {
ThreadPool.QueueUserWorkItem (_ => {
WebClient client = new WebClient ();
Client. DownloadFile (
"http://www.microsoft.com", "index.html");
}
This code example fixes a problem with the first version: Now the Button_Click event does not block the UI thread, but there are three serious problems with the thread-based solution. Let's take a closer look at these issues. problem 1: Wasting thread pool threads
The workaround I just described uses a thread from the thread pool to send the request to the server and wait for the server to respond.
Thread pool threads remain blocked until the server responds. The thread cannot return to the thread pool until the call to Webclient.downloadfile is complete. Because the UI is not frozen, blocking the thread pool line turndown blocking the UI thread is much better, but it does waste a thread from the thread pool.
If an application occasionally blocks thread pool threads for a period of time, performance losses can be negligible. However, if your application blocks frequently, its responsiveness can be reduced by the pressure that the thread pool will endure. The thread pool will attempt to deal with this situation by creating more threads, but it can cause considerable performance overhead.
All of the other asynchronous programming patterns described in this article address the problem of wasting thread pool threads. Question 2: return results
Another challenge with threading for asynchronous programming is that the return value from an operation performed on a helper thread becomes slightly messy.
In the original example, the DownloadFile method writes the downloaded Web page to a local file, so it has a void return value. See another version of the problem where you want to assign the received HTML to the Text property of the TextBox (named Htmltextbox) instead of writing the downloaded Web page to a file.
One of the wrong ways to implement the above process is as follows:
// !!!
Broken code!!!
void Button_Click (object sender, RoutedEventArgs e) {
ThreadPool.QueueUserWorkItem (_ => {
WebClient client = New WebClient ();
String html = client. Downloadstring (
"http://www.microsoft.com", "index.html");
Htmltextbox.text = html;}
);
The problem is that the UI control Htmltextbox is modified by thread pool threads. This is an error because only the UI line Cheng has permission to modify the UI. This restriction is present in both WPF and Windows forms for a variety of reasons.
To resolve this issue, you can capture the synchronization environment on the UI thread, and then publish the message to the environment on the thread pool threads:
void Button_Click (object sender, RoutedEventArgs e) {
SynchronizationContext ctx = synchronizationcontext.current;
ThreadPool.QueueUserWorkItem (_ => {
WebClient client = new WebClient ();
String html = client. Downloadstring (
"http://www.microsoft.com");
CTx. Post (state => {
htmltextbox.text = (string) state;}
, HTML);
}
It is important to recognize that the problem of returning values from helper threads is not limited to applications that contain UIs. In general, it is quite complex to return a value from one thread to another thread, and you need to use a synchronization primitive. Issue 3: Combining asynchronous operations
Explicit processing of threads also makes it difficult to combine asynchronous operations. For example, to download multiple pages in parallel, writing synchronization code becomes more difficult and more error-prone.
This class implementation retains the counter of the asynchronous operation that is still executing. You must modify the counter in a thread-safe manner, such as using Interlocked.decrement. Once the counter reaches 0, the code that processes the download executes. All of this will cause quite a lot of code to be error prone.
Needless to say, using a thread-based pattern can even make it harder to implement a more complex composite pattern correctly. Event-based patterns
One common pattern for asynchronous programming using the Microsoft. NET Framework is the event-based model. The event model exposes a method that initiates an asynchronous operation and raises an event when the operation completes.
The event pattern is a convention for exposing asynchronous operations, but it is not an explicit convention, such as through an interface. Class implementations can determine the fidelity of the follow pattern. Figure 1 shows an example of the methods exposed by the proper implementation of event-based asynchronous programming patterns.
Figure 1 Method of event-based pattern
public class Asyncexample {
//synchronous methods.
public int Method1 (string param);
public void Method2 (double param);
Asynchronous methods.
public void Method1Async (string param);
public void Method1Async (string param, Object userState);
public event Method1completedeventhandler method1completed;
public void Method2async (double param);
public void Method2async (double param, Object userState);
public event Method2completedeventhandler method2completed;
public void CancelAsync (object userState);
public bool IsBusy {get;}
Class implementation not shown.
...
}
WebClient is a class in the. NET Framework that enables asynchronous operations through event-based patterns. To provide an asynchronous variant of the Downloadstring method, WebClient exposes the DownloadStringAsync and CancelAsync methods and downloadstringcompleted events. The following code shows how to implement our example in an asynchronous fashion: <