C#異步編程

 

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

同步方法調用在程序繼續執行之前需要等待同步方法執行完畢返回結果
異步方法則在被調用之後立即返回以便程序在被調用方法完成其任務的同時執行其它操作

異步編程概覽

.NET Framework 允許您異步調用任何方法。定義與您需要調用的方法具有相同簽名的委託;公共語言運行庫將自動爲該委託定義具有適當簽名

的 BeginInvoke 和 EndInvoke 方法。www.elivn.com

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(nullnull);
            
//同步開始,
            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的異步操作。

首先是異步獲得ResponseStream,然後異步讀取數據。

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

 

using System;
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);
    
const int BUFFER_SIZE = 1024;

    
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(
"示例::");
        Console.WriteLine(
"ClientGetAsync http://www.microsoft.com/net/");
    }


    
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;
    }

}


 

總結

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

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

 

在這裏有回調函數,且異步回調中又有異步操作。
轉載請註明出處! http://www.elivn.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章