天天看點

C#異步程式設計(轉)

< DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd>

同步方法和異步方法的差別

同步方法調用在程式繼續執行之前需要等待同步方法執行完畢傳回結果

異步方法則在被調用之後立即傳回以便程式在被調用方法完成其任務的同時執行其它操作

異步程式設計概覽

.NET Framework 允許您異步調用任何方法。定義與您需要調用的方法具有相同簽名的委托;公共語言運作庫将自動為該委托定義具有适當簽名

的 BeginInvoke 和 EndInvoke 方法。

BeginInvoke 方法用于啟動異步調用。它與您需要異步執行的方法具有相同的參數,隻不過還有兩個額外的參數(将在稍後描述)。

BeginInvoke 立即傳回,不等待異步調用完成。

BeginInvoke 傳回 IasyncResult,可用于監視調用進度。

EndInvoke 方法用于檢索異步調用結果。調用 BeginInvoke 後可随時調用 EndInvoke 方法;如果異步調用未完成,EndInvoke 将一直阻塞到

異步調用完成。EndInvoke 的參數包括您需要異步執行的方法的 out 和 ref 參數(在 Visual Basic 中為 <Out> ByRef 和 ByRef)以及由

BeginInvoke 傳回的 IAsyncResult。

四種使用 BeginInvoke 和 EndInvoke 進行異步調用的常用方法。調用了 BeginInvoke 後,可以:

1.進行某些操作,然後調用 EndInvoke 一直阻塞到調用完成。

2.使用 IAsyncResult.AsyncWaitHandle 擷取 WaitHandle,使用它的 WaitOne 方法将執行一直阻塞到發出 WaitHandle 信号,然後調用

EndInvoke。這裡主要是主程式等待異步方法,等待異步方法的結果。

3.輪詢由 BeginInvoke 傳回的 IAsyncResult,IAsyncResult.IsCompeted确定異步調用何時完成,然後調用 EndInvoke。此處理個人認為與

相同。

4.将用于回調方法的委托傳遞給 BeginInvoke。該方法在異步調用完成後在 ThreadPool 線程上執行,它可以調用 EndInvoke。這是在強制裝

換回調函數裡面IAsyncResult.AsyncState(BeginInvoke方法的最後一個參數)成委托,然後用委托執行EndInvoke。

警告   始終在異步調用完成後調用 EndInvoke。

以上有不了解的稍後可以再了解。

例子

1)先來個簡單的沒有回調函數的異步方法例子

請再運作程式的時候,仔細看注釋,對了解很有幫助。還有,若将注釋的中的兩個方法都同步,你會發現異步運作的速度優越性。

using System;

namespace ConsoleApplication1

{

     class Class1

     {

         //聲明委托

         public delegate void AsyncEventHandler();

         //異步方法

         void Event1()

        {

            Console.WriteLine("Event1 Start");

            System.Threading.Thread.Sleep(4000);

            Console.WriteLine("Event1 End");

        }

        // 同步方法

        void Event2()

            Console.WriteLine("Event2 Start");

            int i=1;

            while(i<1000)

            {

                i=i+1;

                Console.WriteLine("Event2 "+i.ToString());

            }

            Console.WriteLine("Event2 End");

        [STAThread]

        static void Main(string[] args)

            long start=0;

            long end=0;

            Class1 c = new Class1();

            Console.WriteLine("ready");

            start=DateTime.Now.Ticks;

            //執行個體委托

            AsyncEventHandler asy = new AsyncEventHandler(c.Event1);

            //異步調用開始,沒有回調函數和AsyncState,都為null

            IAsyncResult ia = asy.BeginInvoke(null, null);

            //同步開始,

            c.Event2();

            //異步結束,若沒有結束,一直阻塞到調用完成,在此傳回該函數的return,若有傳回值。

            asy.EndInvoke(ia);

            //都同步的情況。

            //c.Event1();

            //c.Event2();

            end =DateTime.Now.Ticks;

            Console.WriteLine("時間刻度差="+ Convert.ToString(end-start) );

            Console.ReadLine();

    }

}

2)下面看有回調函數的WebRequest和WebResponse的異步操作。

using System.Net;

using System.Threading;

using System.Text;

using System.IO;

// RequestState 類用于通過

// 異步調用傳遞資料

public class RequestState

    const int BUFFER_SIZE = 1024;

    public StringBuilder RequestData;

    public byte[] BufferRead;

    public HttpWebRequest Request;

    public Stream ResponseStream;

    // 建立适當編碼類型的解碼器

    public Decoder StreamDecode = Encoding.UTF8.GetDecoder();

    public RequestState()

    {

        BufferRead = new byte[BUFFER_SIZE];

        RequestData = new StringBuilder("");

        Request = null;

        ResponseStream = null;

// ClientGetAsync 發出異步請求

class ClientGetAsync

    public static ManualResetEvent allDone = new ManualResetEvent(false);

    public static void Main(string[] args)

        if (args.Length < 1)

            showusage();

            return;

        // 從指令行擷取 URI

        Uri HttpSite = new Uri(args[0]);

        // 建立請求對象

        HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(HttpSite);

        // 建立狀态對象

        RequestState rs = new RequestState();

        // 将請求添加到狀态,以便它可以被來回傳遞

        rs.Request = wreq;

        // 發出異步請求

        IAsyncResult r = (IAsyncResult)wreq.BeginGetResponse(new AsyncCallback(RespCallback), rs);

        // 将 ManualResetEvent 設定為 Wait,

        // 以便在調用回調前,應用程式不退出

        allDone.WaitOne();

    public static void showusage()

        Console.WriteLine("嘗試擷取 (GET) 一個 URL");

        Console.WriteLine("\r\n用法::");

        Console.WriteLine("ClientGetAsync URL");

        Console.WriteLine("示例::");

    private static void RespCallback(IAsyncResult ar)

        // 從異步結果擷取 RequestState 對象

        RequestState rs = (RequestState)ar.AsyncState;

        // 從 RequestState 擷取 HttpWebRequest

        HttpWebRequest req = rs.Request;

        // 調用 EndGetResponse 生成 HttpWebResponse 對象

        // 該對象來自上面發出的請求

        HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar);

        // 既然我們擁有了響應,就該從

        // 響應流開始讀取資料了

        Stream ResponseStream = resp.GetResponseStream();

        // 該讀取操作也使用異步完成,是以我們

        // 将要以 RequestState 存儲流

        rs.ResponseStream = ResponseStream;

        // 請注意,rs.BufferRead 被傳入到 BeginRead。

        // 這是資料将被讀入的位置。

        IAsyncResult iarRead = ResponseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);

    private static void ReadCallBack(IAsyncResult asyncResult)

        // 從 asyncresult 擷取 RequestState 對象

        RequestState rs = (RequestState)asyncResult.AsyncState;

        // 取出在 RespCallback 中設定的 ResponseStream

        Stream responseStream = rs.ResponseStream;

        // 此時 rs.BufferRead 中應該有一些資料。

        // 讀取操作将告訴我們那裡是否有資料

        int read = responseStream.EndRead(asyncResult);

        if (read > 0)

            // 準備 Char 數組緩沖區,用于向 Unicode 轉換

            Char[] charBuffer = new Char[BUFFER_SIZE];

            // 将位元組流轉換為 Char 數組,然後轉換為字元串

            // len 顯示多少字元被轉換為 Unicode

            int len = rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);

            String str = new String(charBuffer, 0, len);

            // 将最近讀取的資料追加到 RequestData stringbuilder 對象中,

            // 該對象包含在 RequestState 中

            rs.RequestData.Append(str);

            // 現在發出另一個異步調用,讀取更多的資料

            // 請注意,将不斷調用此過程,直到

            // responseStream.EndRead 傳回 -1

            IAsyncResult ar = responseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);

        else

            if (rs.RequestData.Length > 1)

                // 所有資料都已被讀取,是以将其顯示到控制台

                string strContent;

                strContent = rs.RequestData.ToString();

                Console.WriteLine(strContent);

            // 關閉響應流

            responseStream.Close();

            // 設定 ManualResetEvent,以便主線程可以退出

            allDone.Set();

        return;

在這裡有回調函數,且異步回調中又有異步操作。

首先是異步獲得ResponseStream,然後異步讀取資料。

這個程式非常經典。從中可以學到很多東西的。我們來共同探讨。

總結

上面說過,.net framework 可以異步調用任何方法。是以異步用處廣泛。

在.net framework 類庫中也有很多異步調用的方法。一般都是已Begin開頭End結尾構成一對,異步委托方法,外加兩個回調函數和AsyncState參數,組成異步操作的宏觀展現。是以要做異步程式設計,不要忘了委托delegate、Begin,End,AsyncCallBack委托,AsyncState執行個體(在回調函數中通過IAsyncResult.AsyncState來強制轉換),IAsycResult(監控異步),就足以了解異步真谛了。

<a href="http://www.cnblogs.com/tag/c%23%E4%B8%93%E6%A0%8F/feeds">#c#專欄</a>

本文轉自 netcorner 部落格園部落格,原文連結:http://www.cnblogs.com/netcorner/archive/2009/10/01/2912060.html   ,如需轉載請自行聯系原作者