按照操作执行所需的资源类型我们可以将操作分为cpu绑定型操作和I/O绑定型操作前者主要是利用cpu进行密集运算后者大部分操作处理时间花在I/O处理上(比如文件系统网络资源)对于I/O绑定型的操作我们可以充分利用多线程机制让多个操作在各自的线程上并行执行 服务调用就是典型的I/O绑定型操作所以多线程在服务调用具有广泛应用按照异步操作发生的位置可以将wcf应用的异步操作分为下面中类型 异步信道调用客户端通过绑定创建的信道向服务端发送消息从而实现了对服务的调用客户端可以通过代理对象异步的调用信道从而实现异步服务的调用 单向消息交换客户端的信道通过单向的消息交换模式向服务端发送消息消息一旦抵达传输层马上返回从而达到异步服务调用的效果 异步服务实现服务端在具体实现服务操作的时候采用异步调用的方式 一异步服务的调用 异步服务代理的创建 一般的服务代理继承自ClientBase<TChannel>默认情况下是不具有异步服务调用的操作但我们可以通过添加引用的方式创建异步服务代理并对服务进行异步调用 在添加服务引用的时候在弹出的服务引用对话框中点击高级按钮会弹出服务引用设置对话框然后勾选生成异步操作即可 对应生成的服务代理类中就会多出几个方法(下面都以计算的服务为例)以下几个方法在接下来都会介绍到的 [csharp] public partial class CalculatorClient:ClientBase<ICalculator>ICalculator { public event SystemEventHandler<AddCompleteArgs> AddComplete; public IAsyncResult BeginAdd(double xdouble yAsyncCallback callbackobject asuncState) public double EndAdd(SystemIAsyncReuslt result) public AddAsync(double xdouble y) public AddAsync(double xdouble yobject userState) } 其中Add用于同步服务调用BeginAdd/EndAdd则用于异步调用注意应用在BeginAdd方法上的OperationContractAttributes特性的AsyncPattern属性被设置成True 通过BeginXxx/EndXxx进行异步服务调用 调用BeginXxx方法让相应的操作在另一个线程执行方法会立即返回一个IAsyncResult类型的对象此时我们可以在当前线程执行额外的操作异步操作执行的结果通过调用EndXxx方法得到而该方法的参数为调用BeginXxx方法得到的IAsyncResult对象通过添加计算服务引用生成的代理类型CalculatorClient为例 [csharp] CalculatorClient proxy=new CalculatorClient() IAsyncResult asyncResult=proxyBeginAdd(nullnull) //其他操作 //Endxx方法可以根据异步操作可能的执行时间来决定调用执行一旦Endxxx方法被调用后当前线程会被阻塞直到异步操作之行结束 double result=proxyEndAdd(asyncResult) proxyClose() ConsoleWriteLine(x+y={} when x={} and y={}result) 注意一个极端的例子是在调用BeginXxx方法后就直接调用EndXxx方法虽然在不同的线程中 但基本没有起到异步的作用这里可以用回调的方式来解决这个问题 通过回调的方式进行异步服务调用 我们可以看看上面服务代理类中多出来的几个方法中BeginAdd方法 public IAsyncResult BeginAdd(double xdouble yAsyncCallback callbackobject asyncState) ①其中参数类型为AsyncCallback的callbackAsyncCallback是个参数类型为IAsyncResult无返回值的委托 ②参数asyncState可以用于向回调操作传递一些额外的参数在BeginAdd方法中指定的asyncState可以通过IAsyncResult的AsyncState属性获得 [csharp] public delegate void AsyncCallback(IAsyncResult ar) public interface IAsyncResult { object AsyncState {get;} WaitHandle AsyncWaitHandle {get;} bool CompletedSynchronously { get;} bool IsCompleted { get;} } 下面通过一个匿名方法的形式定义回调操作由于在回调操作中输出运算结果时需要用到参与的运算数我们通过BeginAdd方法的asyncState参数实现向回调操作传递数据 在回调操作中通过IAsyncResult对象的AsyncState获得操作数 [csharp] CalculatorClient proxy=new CalculatorClient() proxyBeginAdd( delegate(IAsyncResult asyncResult) { double[] operNums=asyncResultAsyncState as double[]; double result=proxyEndAdd(asyncResult) proxyClose() ConsoleWriteLine(x+y={} when x={} and y={}operNums[]operNums[]result) }new double[] {}) 通过事件注册的方式进行异步服务调用 在那个异步服务代理类中还有两个异步执行的AddAsync方法重载和一个AddComplete事件 public AddAsync(double xdouble y) public AddAsync(double xdouble yobject userState) 其中userState参数和BeginAdd方法的asyncState参数具有相同的作用 [csharp] public event SystemEventHandler<AddCompleteEventArgs> AddComplete; public class AddCompleteEventArgs:EventArgs { public bool Cancelled{get;} public Exception Error{get;} public object UserState {get;} } 事件参数AddCompleteEventArgs定义了个只读属性其中Cancelled表示异步操作是否取消异步操作抛出异常可以通过属性Error得到UserState返回的是Addsync方法中的userState参数 由于AddAsync方法是以异步方式执行的所以调用后会立即返回如果我们注册了CalculatorClient的AddComplete事件他将在异步服务调用结束后被触发所以我们可以按照如下方式调用服务代理Addsync方法 [csharp] CalculatorClient proxy=new CalculatorClient() proxyAddComplete+=delegate(object sender AddCompleteEventArgs args) { double[] operNums=argsUserState as double[]; double result=argsResult; proxyClose() ConsoleWriteLine(x+y={} when x={} and y={}operNums[]operNums[]result) }; proxyAddAsync(new double[]{}) 二异步服务的实现 上面介绍了如何调用异步服务但如何服务操作是如何以异步模式实现呢? 异步操作模式 前面提到过在OperationContractAttributes特性的AsyncPattern属性如果将应用到某个操作的方法上设为True则意味着对应的操作需要采用异步模式来实现实际上客户端生成的异步服务代理类实现的契约接口中也包含了这样的异步操作 异步服务操作是通过BeginXxx/EndXxx两个配对的方法实现的只需将BeginXx方法的特性将属性AsyncPattern设置为True即可 三实例演示 下面通过异步模式来定义一个读取文件的服务 客户端读取 |