《CLR via C#》讀書筆記-異步編程(二)

在《CLR via C#》的27.2小節中使用了命名管道的內容,上一篇是一個使用命名管道的例子,在《CLR via C#》中同樣有一個命名管道的例子(使用異步),具體內容如下:
服務器端的代碼:

internal sealed class PipeServer{
    //聲明一個命名管道
    private readonly NamedPipeServerStream m_pipe = new NamedPipeServerStream(
        "Echo",PipeDirection.InOut,-1,PipeTransmissionMode.Message,
        PipeOptions.Asynchronous|PipeOptions.WriteThrough);

    public PipeServer(){
        m_pipe.BeginWaitForConnection(
    }

    //定義當客戶端連接後的回調方法
    private void ClientConnected(IAsyncResult result){

        new PipeServer();

        //APM的結束
        m_pipe.EndWaitForConnection(result);

        byte[] data = new byte[1000];
        m_pipe.BeginRead(data,0,data.length,GotRequest,data);
    }

    //當數據讀取完畢後的回調方法
    private void GotRequest(IAsyncResult result){
        //EndRead方法返回已經讀取內容的字節數。若返回值爲零,則代表到達流的末尾
        int bytesread=m_pipe.EndRead(result);

        byte[] data=(byte[])result.AsyncState;

        data=Encoding.UTF8.GetBytes(
            Encoding.UTF8.GetString(data,0,bytesread).ToUpper().ToCharArray());

        m_pipe.BeginWrite(data,0,data.Length,WriteDone,null);
    }

    private void WriteDone(IAsyncResult result){
        m_pipe.EndWrite(result);
        m_pipe.Close();
    }
}

通過上面的這個例子,可以看到APM(Asynchronous Programming Model)的編寫模式,在方法的內部,以Endxxx爲開始,以BeginXXX爲結束,在兩者之間是相關的內容處理。
參數說明
對BeginXXX中方法的參數說明:方法的前三個參數是正常的,第四個參數是AsyncCallback,該委託的定義如下:

public delegate void AsyncCallback(IAsyncResult ar)

可以將IAsyncResult看成是IRP的唯一標示。其唯一標示了一個I/O請求,系統在將請求放入驅動程序的隊列中,並返回IAsyncResult對象的一個引用。
IAsyncResult是一個接口,而該接口具有幾個屬性,而實現了該接口的類也一定會存在這幾個屬性。這幾個屬性的具體定義如下:
屬性
IAsyncResult的AsyncState就可以用於數據傳輸,由beginxxx傳入到endxxx中。下面是客戶端的代碼

internal sealed class PipeClient{
    private readonly NamedPipeClientStream m_pipe;

    public PipeClient(string serverName,string message)
    {
        m_pipe=new NamedPipeClientStream(serverName,"Echo",PipeDirection.Inout,PipeOptions.Asynchronous|PipeOptions.WriteThrough);

        m_pipe.Connect();
        //必須先連接,之後才能設定ReadMode
        m_pipe.ReadMode=PipeTransmissionMode.Message;

        byte[] output=Encoding.UTF8.GetBytes(message);

        m_pipe.BeginWrite(output,0,output.Length,WriteDone,null);
    }

    private void WriteDone(IAsyncResult result){
        m_pipe.EndWrite(result);

        byte[] data=new byte[1000];
        m_pipe.BeginRead(data,0,data.Length,GotResponse,data);
    }

    private void GotResponse(IAsyncResult result){
        int bytesRead = m_pipe.EndRead(result);

        byte[] data=(byte[])result.AsyncState;
        Console.WriteLine("服務器的返回值"+Encoding.UTF8.GetString(data,0,data.Length));
        m_pipe.Close();
    }
}

如何使用服務器端和客戶端的程序
使用服務器端的代碼如下:

public static void Main(){
    for(int i=0;i<Environment.ProcessorCount;i++){
        new PipeServer();
    }

    Console.WriteLine("回車結束服務器程序");
    Console.ReadLine();
}

使用客戶端的代碼如下

public static void Main(){
    for(int i=0;i<100;i++)
        new PipeClient(".","請求"+i);

    Console.ReadLine();
}

以上的代碼均來自《CLR via C#》的27.2小節。代碼很簡單,抄錄很麻煩。肩膀都酸了。
小節
APM與其他正常的代碼沒有任何的差別,不管是異常處理還是其他方面,基本上一模一樣。只有一個例外,就是在使用beginxxx和endxxx時略有差別。與多線程相比較,基本上沒有什麼太大的花樣。
在27.2的開頭,反而是講了APM的關鍵信息
1、所有派生自system.io.stream並與硬件通信的類(包括filestream和networkstream)都提供APM
2、system.net.dns類提供了幾個方法
3、system.net.sockets.socket類中提供符合APM編程方式的方法
另外,所有委託都定義了一個begininvoke方法。
總的來說,APM編碼方式是一種考慮了系統穩定性的編碼方式。其作用是讓系統更加穩定。與正常使用的方法沒有多大的差別。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章