Skip to content

使用 APM(Asynchronous Programming Model)异步委托编程模型

🏷️ C#

异步编写模型是一种模式,该模式允许用更少的线程去做更多的操作,.NET Framework 很多类也实现了该模式,同时我们也可以自定义类来实现该模式,(也就是在自定义的类中实现返回类型为 IAsyncResult 接口的 BeginXXX 方法和 EndXXX 方法),另外委托类型也定义了 BeginInvokeEndInvoke 方法,并且我们使用 WSDL.exeSvcUtil.exe 工具来生成 Web 服务的代理类型时,也会生成使用了 APM 的 BeginXxxEndXxx 方法。

本示例是一个控制台程序,异步读取网页并输出到控制台。用到了下面几个异步方法:

  1. HttpWebRequest.BeginGetResponse

  2. HttpWebRequest.EndGetResponse

  3. Stream.BeginRead

  4. Stream.EndRead

看一下 HttpWebRequest.BeginGetResponse 的方法定义:

cs
public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state);

返回值为 IAsyncResult 类型,第一个参数为 AsyncCallback 类型。

AsyncCallback 是个代理,其定义如下(一个参数类型为 IAsyncResult 的代理):

cs
public delegate void AsyncCallback(IAsyncResult ar);

IAsyncResult 接口的定义如下:

cs
public interface IAsyncResult
{
    object AsyncState { get; }
    WaitHandle AsyncWaitHandle { get; }
    bool CompletedSynchronously { get; }
    bool IsCompleted { get; }
}

其中 AsyncState 中的值即是 BeginGetResponse 方法调用时传进来的 state 参数。

示例代码

Program.cs

cs
using System;
using System.IO;
using System.Net;
using System.Text;

namespace APMSample
{
    class Program
    {
        static void Main(string args)
        {
            DownloadFileAsync("http://baidu.com/");
            Console.WriteLine("Start DownLoad the Page.");
            Console.ReadLine();
        }

        private static void DownloadFileAsync(string url)
        {
            try
            {
                HttpWebRequest myHttpWebRequest = WebRequest.Create(url) as HttpWebRequest;
                RequestState requestState = new RequestState();
                requestState.Request = myHttpWebRequest;
                myHttpWebRequest.BeginGetResponse(ResponseCallback, requestState);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error Message is {0}", e.Message);
            }
        }

        private static void ResponseCallback(IAsyncResult ar)
        {
            RequestState myRequestState = ar.AsyncState as RequestState;
            HttpWebRequest myHttpWebRequest = myRequestState.Request;
            myRequestState.Response = myHttpWebRequest.EndGetResponse(ar) as HttpWebResponse;
            Stream responseStream = myRequestState.Response.GetResponseStream();
            myRequestState.ResponseStream = responseStream;
            IAsyncResult asynchronousRead = responseStream.BeginRead(myRequestState.BufferRead, 0, myRequestState.BufferRead.Length, ReadCallback, myRequestState);
        }

        private static void ReadCallback(IAsyncResult ar)
        {
            try
            {
                RequestState myRequestState = ar.AsyncState as RequestState;
                Stream responseStream = myRequestState.ResponseStream;
                int readSize = responseStream.EndRead(ar);
                if (readSize > 0)
                {
                    Console.Write(Encoding.UTF8.GetString(myRequestState.BufferRead));
                    myRequestState.BufferRead = new byte[myRequestState.BufferRead.Length];
                    responseStream.BeginRead(myRequestState.BufferRead, 0, myRequestState.BufferRead.Length, ReadCallback, myRequestState);
                }
                else
                {
                    Console.WriteLine();
                    Console.WriteLine("DownLoad Completely.");
                    myRequestState.Response.Close();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error Message is:{0}", e.Message);
            }
        }
    }
}

RequestState.cs

cs
using System.IO;
using System.Net;

namespace APMSample
{
    public class RequestState
    {
        public HttpWebRequest Request { get; set; }
        public HttpWebResponse Response { get; set; }
        public Stream ResponseStream { get; set; }
        public byte BufferRead { get; set; } = new byte[1000];
    }
}

输出结果

html
Start DownLoad the Page.
<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>

DownLoad Completely.

参考文章