1. 程式人生 > >15.1 非同步函式簡介與思考

15.1 非同步函式簡介與思考

 

 1     public partial class AsyncForm : Form
 2     {
 3         Label label;
 4         Button button;
 5         public AsyncForm()
 6         {
 7             InitializeComponent();
 8             label = new Label { Location = new Point(this.Size.Width / 2, 20), Text = "Length" };
9 button = new Button { Location = new Point(this.Size.Width / 2, 50), Text = "Click" }; 10 button.Click += DisPlayWebSiteLength; 11 AutoSize = true; 12 Controls.Add(label); 13 Controls.Add(button); 14 } 15 async void DisPlayWebSiteLength(object
sender, EventArgs e) 16 { 17 label.Text = "Fetching"; 18 using (HttpClient client = new HttpClient()) 19 { 20 Task<string> task = client.GetStringAsync("https://www.baidu.com/"); 21 string text = await task; 22 label.Text = text.Length.ToString();
23 } 24 } 25 }

注意, task 的型別是 Task<string> ,而 await task 表示式的型別是 string 。也就是說,
await 表示式執行的是“拆包”(unwrap)操作,至少在被等待的值為 Task<TResult> 時是這樣。
(還可以等待其他型別,但 Task<TResult> 是一個不錯的起點。)這是 await 的一個方面,看上
去跟非同步程式設計沒什麼直接關係,但卻讓生活更加輕鬆。
await 的主要目的是在等待耗時操作完成時避免阻塞。它是如何在具體執行緒中工作的呢?我
們在方法的開始和結束處都設定了 label.Text ,所以可以很自然地認為這兩條語句都在UI執行緒
執行。但顯然我們在等待頁面下載的時候沒有阻塞UI執行緒。

後續操作 後續操作指在非同步操作(或任何 Task )完成時執行的回撥程式。在非同步方法
中,後續操作保留了方法的控制狀態。就像閉包保留了環境中的變數一樣,後續操作記
住了它的位置,因此在執行時可回到原處。 Task 類包含一個專門用於新增後續操作的方
法,即 Task.ContinueWith 。

如果在 await 表示式後的程式碼中加入斷點(假設 await 表示式需要後續操作),你會發現堆
棧跟蹤中不再擁有 Button.OnClick 方法,該方法早已執行完畢。現在的呼叫棧是純粹的
Windows Forms事件迴圈,在頂部還有一些非同步基本結構。如果為了適時地更新UI,而在後臺線
程中呼叫 Control.Invoke ,將得到跟現在非常類似的呼叫棧,然而現在這些工作已經完全為我
們做好了。起初發現呼叫棧在眼皮底下發生如此顯著的變化時,你可能會有些沮喪,但這對非同步
程式設計來說是絕對必要的。

15.2.2 非同步方法

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             PrintPageLength();
 6 
 7             Console.ReadKey();
 8         }
 9 
10         static void PrintPageLength()
11         {
12             Task<int> task = GetPageLengthAsync("https://www.baidu.com/");
13             Console.WriteLine(task.Result);
14         }
15 
16         async static Task<int> GetPageLengthAsync(string url)
17         {
18             using (HttpClient client = new HttpClient())
19             {
20                 Task<string> task = client.GetStringAsync(url);
21                 int lenth = (await task).Length;
22                 return lenth;
23 
24             }
25         }
26     }