C# 1.0 新特性之異步委託(AP、APM)

Ø  前言編程

C# 異步委託也是屬於異步編程中的一種,能夠稱爲 Asynchronous Programming(異步編程)或者 Asynchronous Programming Model(異步編程模型),由於這是實現異步編程的模式。委託是 C#1.0 就有的特性,而且 .NET v1.0 同時也伴隨有 AsyncCallbackIAsyncResult 等類/接口的出現,因此全部的 .NET 版本中都是支持的。異步

 

1.   什麼是異步委託async

1)   異步委託是採用異步回調的方式實現異步執行,當使用委託異步執行某個方法時,將從線程池中取出一個線程去執行該方法。ide

2)   當執行完成後則調用 AsyncCallback 委託指定的方法,完成異步回調。異步編程

3)   開始執行一個異步委託後,能夠使用4種方式等待異步執行完成:spa

1.   開啓異步委託後,BeginInvoke() 方法將返回一個實現了 IAsyncResult 接口的 System.Runtime.Remoting.Messaging.AsyncResult 對象。使用該對象的 AsyncWaitHandle 屬性,並調用 WaitOne() 方法,該方法會阻塞當前線程,直到收到信號(異步委託方法執行完成)。線程

2.   調用委託對象的 EndInvoke() 方法,須要傳遞一個 AsyncResult 對象,該方法也用於獲取異步委託的返回值,因此這種方式也會阻塞當前線程。3d

3.   使用 IAsyncResult.IsCompleted 屬性,判斷是否執行完成。該屬性在異步委託方法執行完成時 true.orm

4.   【推薦】使用異步回調委託的方式,當異步委託方法執行完成後調用,若是在不須要非要等到異步完成時獲取返回結果的狀況下,推薦使用該方式。對象

 

2.   下面分別使用這四種方式等待

首先,定義四個委託類型。

public delegate void MyDelegate1();

public delegate string MyDelegate2();

public delegate void MyDelegate3(string str);

public delegate int MyDelegate4(int num1, int num2);

 

1)   使用 WaitOne() 方法(匿名方法)

/// <summary>

/// 使用 WaitOne() 方法(匿名方法)。

/// </summary>

public void AsyncDelegateTest1()

{

    WriteLine("AsyncDelegateTest1() 方法開始執行,線程Id{0}", GetThreadId());

    MyDelegate1 d1 = new MyDelegate1(delegate()

    {

        WriteLine("匿名方法開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

        Thread.Sleep(3000);

        WriteLine("匿名方法結束執行,線程Id{0}", GetThreadId());

    });

    IAsyncResult ar = d1.BeginInvoke(null, null);

    ar.AsyncWaitHandle.WaitOne();   //這裏將阻塞線程,直到收到信號(異步方法執行完成)

    WriteLine("AsyncDelegateTest1() 方法結束執行,線程Id{0}{1}", GetThreadId(), GetTime());

}

運行以上代碼:

clip_image002[4]

 

2)   使用委託對象的 EndInvoke() 方法(匿名方法)

/// <summary>

/// 使用委託對象的 EndInvoke() 方法(匿名方法)。

/// </summary>

public void AsyncDelegateTest2()

{

    WriteLine("AsyncDelegateTest2() 方法開始執行,線程Id{0}", GetThreadId());

    MyDelegate2 d2 = new MyDelegate2(delegate()

    {

        WriteLine("匿名方法開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

        Thread.Sleep(3000);

        WriteLine("匿名方法結束執行,線程Id{0}", GetThreadId());

        return System.Reflection.MethodBase.GetCurrentMethod().Name;

    });

    IAsyncResult ar = d2.BeginInvoke(null, null);

    string result = d2.EndInvoke(ar);   //這裏也將阻塞線程,直到異步方法執行完成

    WriteLine("AsyncDelegateTest2() 方法結束執行,{0},異步委託返回結果:{1}", GetTime(), result);

}

運行以上代碼:

clip_image004[4]

 

3)   使用 IAsyncResult.IsCompleted 屬性(Lambda 表達式)

/// <summary>

/// 使用 IAsyncResult.IsCompleted 屬性(Lambda 表達式)。

/// </summary>

public void AsyncDelegateTest3()

{

    WriteLine("AsyncDelegateTest3() 方法開始執行,線程Id{0}", GetThreadId());

    MyDelegate3 d3 = new MyDelegate3((str) =>

    {

        WriteLine("Lambda 表達式開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

        Thread.Sleep(3000);

        WriteLine("Lambda 表達式結束執行,str{0}", str);

    });

    IAsyncResult ar = d3.BeginInvoke("這是一段話!", null, null);

    while (!ar.IsCompleted) //標記是否完成(其實與直接調 EndInvoke() 方法沒什麼區別)

    { }

    WriteLine("AsyncDelegateTest3() 方法結束執行,線程Id{0},{1}", GetThreadId(), GetTime());

}

運行以上代碼:

clip_image005[4]

 

4)   【推薦】使用異步回調委託

/// <summary>

/// 【推薦】使用異步回調委託。

/// </summary>

public void AsyncDelegateTest4()

{

    WriteLine("AsyncDelegateTest4() 方法開始執行,線程Id{0}", GetThreadId());

    MyDelegate4 d4 = new MyDelegate4(Add);

    //這裏必須將第二個參數(委託對象)傳入,不然異步回調中 IAsyncResult.AsyncState 屬性將爲 null.

    IAsyncResult ar = d4.BeginInvoke(22, 36, new AsyncCallback(AddCallback), d4);

    WriteLine("AsyncDelegateTest4() 方法結束執行,線程Id{0}", GetThreadId());

}

 

public int Add(int num1, int num2)

{

    WriteLine("Add() 方法開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

    Thread.Sleep(3000);

    WriteLine("Add() 方法結束執行,線程Id{0}", GetThreadId());

    return num1 + num2;

}

 

public void AddCallback(IAsyncResult ar)

{

    WriteLine("AddCallback() 方法開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

    MyDelegate4 d4 = ar.AsyncState as MyDelegate4;  //獲取委託對象

    int result = d4.EndInvoke(ar); //這裏並不會阻塞

    WriteLine("AddCallback() 方法結束執行,計算結果:{0}{1}", result, GetTime());

}

運行以上代碼:

clip_image007[4]

 

3.   下面,再來看下 C# 中一些經常使用基於異步回調的運用

1)   模擬 Web 請求,異步讀取響應流

/// <summary>

/// 異步獲取網頁 HTML 內容。

/// </summary>

public void AsyncGetHtmlString()

{

    WriteLine("AsyncGetHtmlString() 方法開始執行,線程Id{0}{1}", GetThreadId(), GetTime());

    WebRequest request = WebRequest.Create("http://www.cnblogs.com/abeam/");

    request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);

    WriteLine("AsyncGetHtmlString() 方法結束執行,線程Id{0}", GetThreadId());

}

 

public async void ResponseCallback(IAsyncResult ar)

{

    WriteLine("ResponseCallback() 方法開始執行(此時已經得到響應),線程Id{0}{1}", GetThreadId(), GetTime());

    WebRequest request = ar.AsyncState as WebRequest;

    using (WebResponse response = request.EndGetResponse(ar))

    {

        using (var stream = response.GetResponseStream())

        {

            WriteLine("開始異步讀取,線程Id{0}{1}", GetThreadId(), GetTime());

            WriteLine("響應的 HTML 內容:");

            int count, totalCount = 0;

 

            //1. 同步讀取響應流

            using (var sr = new System.IO.StreamReader(stream, Encoding.UTF8))

            {

                char[] chars = new char[256];

                while ((count = sr.Read(chars, 0, chars.Length)) > 0)

                {

                    totalCount += count;

                    if (totalCount <= chars.Length) //太多屏幕容不下

                    {

                        string content = new string(chars, 0, count);

                        WriteLine(content);

                        WriteLine("同步讀取流線程Id{0}", GetThreadId());

                    }

                }

            }

            WriteLine("響應的 HTML 總字符數:{0}", totalCount);

 

            //2. 異步讀取響應流

            /*

                * byte[] buffer = new byte[stream.Length];

                * int totalCount = await stream.ReadAsync(buffer, 0, buffer.Length);

                * 不能使用 stream.Length,由於 stream 是一種 System.Net.ConnectStream,否者將報異常:

                * 未處理System.NotSupportedException

                * Message: System.NotSupportedException」類型的未經處理的異常在 mscorlib.dll 中發生

                * 其餘信息: 此流不支持查找操做。

                */

            byte[] buffer = new byte[1024];

            while ((count = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)

            {

                totalCount += count;

                if (totalCount <= 1024)  //太多屏幕容不下

                {

                    string content = Encoding.UTF8.GetString(buffer);

                    Write(content);

                }

                WriteLine();

                Write("異步讀取流線程Id{0}", GetThreadId());

            }

            WriteLine();

            WriteLine("響應的 HTML 總字節數:{0}", totalCount);

        }

    }

}

下面是兩種讀取方式的結果:

clip_image009[4]

clip_image011[4]

 

Ø  總結

1.   異步委託主要使用 BeginInvoke() 方法開啓異步委託,該方法傳入一個回調委託 AsyncCallback 對象。

2.   BeginInvoke() 返回一個實現了 IAsyncResult 接口的對象,能夠使用該對象的 AsyncWaitHandle.WaitOne() 方法和 IsCompleted 屬性判斷異步是否完成。

3.   一樣 AsyncCallback 委託的簽名也有個 IAsyncResult 參數,該委託將在異步調用完成時執行

4.   須要獲取異步委託的返回結果,都必須調用 EndInvoke() 方法。

相關文章
相關標籤/搜索