使用 APM(Asynchronous Programming Model)异步委托编程模型
🏷️ C#
异步编写模型是一种模式,该模式允许用更少的线程去做更多的操作,.NET Framework 很多类也实现了该模式,同时我们也可以自定义类来实现该模式,(也就是在自定义的类中实现返回类型为 IAsyncResult
接口的 BeginXXX
方法和 EndXXX
方法),另外委托类型也定义了 BeginInvoke
和 EndInvoke
方法,并且我们使用 WSDL.exe
和 SvcUtil.exe
工具来生成 Web 服务的代理类型时,也会生成使用了 APM 的 BeginXxx
和 EndXxx
方法。
本示例是一个控制台程序,异步读取网页并输出到控制台。用到了下面几个异步方法:
HttpWebRequest.BeginGetResponse
HttpWebRequest.EndGetResponse
Stream.BeginRead
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.