[.net 物件導向程式設計進階] (19) 非同步(Asynchronous) 使用非同步建立快速響應和可伸縮性的應用程式

yubinfeng發表於2015-07-30

[.net 物件導向程式設計進階] (19) 非同步(Asynchronous) 使用非同步建立快速響應和可伸縮性的應用程式

本節導讀:

本節主要說明使用非同步進行程式設計的優缺點及如何通過非同步程式設計.

使用傳統方法BeginInvoke / EndInvoke來實現非同步。

使用async/await 建立非同步方法和事件。

通過非同步程式設計建立具有快速響應和可伸縮性的應用程式。 

讀前必備:

A.委託 [.net 物件導向程式設計基礎]  (21) 委託

B.事件 [.net 物件導向程式設計基礎] (22) 事件

1.非同步程式設計的優缺點:

A.讓使用者介面快速響應;對於耗時操作阻塞UI執行緒,通過非同步回撥可使用UI快速響應。

B.建立高伸縮性的應用。對於服務端應用,建立更多執行緒來處理消耗資源較多,使用非同步可使用主執行緒繼續工作,不需要等待返回。使用程式具有更好的伸縮性。

對於非同步的缺點,最主要一點就是比建立同步程式難度大一些,首先使用傳統的方法建立非同步,比起同步更容易出錯。不過隨著.NET的不斷髮展和第三方非同步元件的豐富,建立非同步應用程式也變得越來越簡單了。

2.非同步的實現

對於.NET中的非同步程式設計,.NET在各個方向都幾乎提供了同步和非同步的兩個方式來實現,在這裡我們不能把.NET中全部的非同步程式設計方法都列舉出來了,下面介紹幾種常用且實用的非同步方法。

3.使用BeginInvoke / EndInvoke實現非同步

3.1 簡單的非同步示例

下面看一個簡單的示例: 

//使用一個有返回值的泛型委託來執行BeginInvoke
Func<string> myFunc = new Func<string>(()=>{
    Thread.Sleep(10);
    return "我是非同步執行完成的返回值 當前時間:" + System.DateTime.Now.ToString();
});
IAsyncResult asynResult = myFunc.BeginInvoke(null, null);
//在非同步沒有完成前,可以做別的事
while (!asynResult.IsCompleted)
{
    //當不是true時,就執行這裡的程式碼
    Console.WriteLine("當前非同步是否完成:" + asynResult.IsCompleted + " 當前時間:" + System.DateTime.Now.ToString());
}
string result = myFunc.EndInvoke(asynResult);//當是true時,就將結果返回顯示

Console.WriteLine(result);

 執行結果如下:

 

在非同步沒有完成時,可以繼續工作做一些想做的事,非同步完成後返回結果。

3.2 使用非同步的超時 WaitOne 判斷非同步完成

除了上面使用IsCompleted來判斷非同步完成之外,也可以使用超時來判斷非同步的完成情況

示例如下 :

//使用一個有返回值的泛型委託來執行BeginInvoke
Func<string> myFunc = new Func<string>(()=>{
    int i = 0;
    while (i<99999999)
        ++i;
    return "非同步執行完成的返回值" + (i).ToString() + " 當前時間:" + System.DateTime.Now.ToString();
   
});
IAsyncResult asynResult = myFunc.BeginInvoke(null, null);

while (!asynResult.AsyncWaitHandle.WaitOne(10, false))
    Console.Write("*");

string result = myFunc.EndInvoke(asynResult);
Console.Write("\n");
Console.WriteLine(result);

執行結果如下:

3.3 回撥

畢竟上述兩種等待不是一個好的方法。我們在前端開發中使用過ajax的同學肯定知道,前端中非同步使用一個回撥函式在非同步完成後完成我們想要做的事,.NET自然也有類似的回撥方法,

看示例:

           
//使用一個有返回值的泛型委託來執行BeginInvoke
Func<string> myFunc = new Func<string>(()=>{
    int i = 0;
    while (i<99999999)
        ++i;
    return "非同步執行完成的返回值" + (i).ToString() + " 當前時間:" + System.DateTime.Now.ToString();
   
});
IAsyncResult asynResult = myFunc.BeginInvoke((result) =>
{
    string rst = myFunc.EndInvoke(result);
    Console.WriteLine("非同步完成了,我該返回結果了!");
    Console.WriteLine(rst);
}, null);

執行結果如下 :

3.4 其它元件中的Begin\End非同步方法

除了BeginInvoke / EndInvoke之外,.NET在很多類中提供了非同步的方法,

如System.Net.HttpWebRequest類的BeginGetResponse和EndGetResponse方法,

這裡不再一一列舉了,使用方法和上面的示例類似。

 4. async/await  

.NET 5.0 以後,讓非同步程式設計變得更加簡單了,我們介紹一下async和await。 

它讓我們編寫非同步程式變得和同步一樣簡單,不但減少了程式碼量,而且不會因為非同步讓我們程式邏輯被打亂。 

4.1 非同步方法 

下面使用async 和 await關鍵字來建立一個非同步方法,

在第一個方法裡呼叫第二個非同步方法,

第二個非同步方法中使用了多執行緒。

聽起來很繞口,不過整個程式碼編寫和同步方法沒有什麼區別,只是多一個關鍵字。

static void Main(string[] args)
{
    Console.WriteLine("主執行緒開始..");
    AsyncMethod();
    Thread.Sleep(1000);
    Console.WriteLine("主執行緒結束..");


    Console.ReadKey();
}

static async void AsyncMethod()
{
    Console.WriteLine("開始非同步方法");
    var result = await MyMethod();
    Console.WriteLine("非同步方法結束");
}

static async Task<int> MyMethod()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("非同步執行" + i.ToString() + "..");
        await Task.Delay(1000); //模擬耗時操作
    }
    return 0;
}

執行結果如下: 

 

4.2 非同步事件

下面使用一個WinForm應用程式來測試一下非同步事件,我們建立一個同步的Click事件和一個非同步的Click事件,先觸發非同步,然後緊接著觸發同步,看一下執行結果。 

//同步事件
private void button2_Click(object sender, EventArgs e)
{
    textBox1.Text += "同步執行開始..\r\n";          
    MyMethodFirst();
    textBox1.Text += "同步執行結束..\r\n";          
}
//同事事件呼叫方法
int MyMethodFirst()
{
    for (int i = 0; i < 5; i++)
    {
        textBox1.Text += "同步執行" + i.ToString() + "..\r\n";               
    }
    return 0;
}

//非同步事件
private async  void button3_Click(object sender, EventArgs e)
{
    textBox1.Text += "非同步執行開始..====\r\n";
    await MyMethodSencond();
    textBox1.Text += "非同步執行結束..====\r\n";     
}
//非同步事件呼叫方法
async Task<int> MyMethodSencond()
{
    for (int i = 0; i < 5; i++)
    {
        textBox1.Text += "非同步執行" + i.ToString() +" ..====\r\n";          
        await Task.Delay(1000); //模擬耗時操作
    }
    return 0;
}

 執行結果如下:

5. 本節要點

A.使用傳統方法BeginInvoke / EndInvoke來實現非同步

B.使用async/await 建立非同步方法和事件

==============================================================================================

返回目錄

<如果對你有幫助,記得點一下推薦哦,如有有不明白或錯誤之處,請多交流>

<對本系列文章閱讀有困難的朋友,請先看《.net 物件導向程式設計基礎》>

<轉載宣告:技術需要共享精神,歡迎轉載本部落格中的文章,但請註明版權及URL>

.NET 技術交流群:467189533 .NET 程式設計

==============================================================================================

相關文章