代码https://yunpan.cn/cPns5DkGnRGNs 密码:3913 我们接着上一张的内容去讲,上一张的最后,看到了我们的程序报错了。 这里我们解释一下为什么报错,为什么会出现死锁呢?异常:究其本质,这是一个死锁导致的异常,由于默认的情况是服务 ...
代码
https://yunpan.cn/cPns5DkGnRGNs 密码:3913
我们接着上一张的内容去讲,上一张的最后,看到了我们的程序报错了。
这里我们解释一下为什么报错,为什么会出现死锁呢?
异常:究其本质,这是一个死锁导致的异常,由于默认的情况是服务按Single并发模式执行(在服务器执行过程中,服务对象只能被一个get='_blank'>线程访问。WCF通过加锁的机制保证服务对象的独占性使用,也就是说在服务执行开始时会对服务对象加锁,该锁在服务操作结束之后释放)
在Add操作执行的过程中,服务端会回调客户端操作进行运算结果的显示工作。
如果回调采用单向操作:
回调请求一经发出便会立即返回,不需要等待服务端的反馈,操作可以继续得到执行,直到操作正常结束。而另一方面的服务端,当调用操作在客户端正常执行后,需要反馈回到服务端所以试图访问操作的时候,发现对象被服务操作执行的线程锁定,所以他会等待服务操作执行完成后将锁释放。这样,服务操作需要等待回调操作进行正常的返回以便执行后续操作,而回调操作只有等待服务操作执行完成之后将锁释放才可以访问。所以就造成了死锁状态。
通俗讲就是 因为客户端第一次调用服务端的Add方法的线程正在执行并且执行到了等待客户端的反馈信息,就这样一直的等待,资源不会被释放以至于处于锁定状态,而客户端也执行完成了相关操作,需要进行反馈服务端信息,但是服务端一直处于锁定状态,而无法再次请求。两边就产生了 矛盾。所以死锁了。
那么怎么解决上面的问题呢?
使用多线程或者异步操作
多线程与异步操作
按照操作执行所需要的资源类型,我们可以将操作分为CPU绑定型操作和I/O操作。对于前者,操作的执行主要利用CPU进行密集的计算:而对于后者,大部分的操作处理时间花在I/O操作处理,比如访问数据库,文件系统,网络资源等。对于I/O的操作,我们可以充分利用多线程的机制,让多个操作在自己的线程并发执行,从而提高系统性能和响应能力。服务调用就是典型的I/O操作,所以多线程在服务调用中具有广泛的应用。
如果按照异步操作发生的位置,可以将WCF应用的异步操作分为下面2种情况:
1:异步信道调用:
客户端通过绑定创建的信道向服务端发送消息,从而实现了对服务的调用。客户端也可以通过代理对象异步地调用信道,从而实现异步服务调用。
2:单向(One-Way)消息交换 (这一种我们第四讲已经说过了,只是没有点透,这里说单向消息交换 与 第一种 异步信道 其实是 异曲同工 的效果 )
客户端信道通过单向的消息交换模式向服务端发送消息,消息一旦抵达传输层马上返回,从而达到异步服务调用的效果。
我们重点说说 异步信道的调用:
为了方便客户端进行异步的服务调用,最简单的方式是通过添加服务引用的方式来创建(第一讲我们说过)。通过该方式来创建异步服务代理,创建的方式只需要在添加服务引用对话框中点击“高级”按钮,便会填出一个“服务引用设置”对话框,勾选“生成异步操作”复选框即可。
[ 5-01 ]
[ 5-02 ]
不管 勾不勾选 " 允许生成异步操作 " 的复选框 都会生成一个继承自ClientBase<TChannel>的类,但 勾选后 所不同的是,该类中会多出一些与异步服务调用相关的成员。 在具体通过服务代理进行异步服务调用的时候,可以采用不同的调用方式,不仅可以采用参数典型的BeginXxx和EndXxx的形式,也可以采用回调的形式,还可以采用事件注册的方式。
例如:我们的契约中有个Add的方法,如果勾选了 " 允许生成异步操作 " 的复选框 就会多出一个 BeginAdd 和 EndAdd 的方法,这两个方法就是异步操作的方法。
我们做一个异步服务调用的小Demo,代码还是在 云盘,自己去找:
说明一下,异步服务调用 的发起者 一定是 客户端,所以 其它地方 不需要额外说明,与之前一样不变的代码
[ 5-03 ]
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 8 namespace Client 9 {10 class Program11 {12 static void Main(string[] args)13 {14 15 16 //必须先通过 先 添加服务引用17 18 //通过BeginXxx/EndXxx进行异步服务调用19 //在调用BeginAdd方法后,可以做一些额外的处理工作,这些工作将会和Add服务操作的调用并发地运行,最终的运算结果通过EndAdd方法得到20 21 //创建 服务引用的 代理22 ServiceCalculator.CalculatorClient proxy = new Client.ServiceCalculator.CalculatorClient();23 //异步操作24 IAsyncResult asynResult = proxy.BeginAdd(1, 2, null, null);25 //得到返回的结果26 double result = proxy.EndAdd(asynResult);27 proxy.Close();28 Console.WriteLine("x+y={2} when x={0} and y={1}", 1, 2, result);29 Console.Read();30 31 32 /*33 其实上面的方法并不好34 是当EndAdd方法被执行的时候 如果异步执行的Add方法(也就是调用服务端的方法)还没有执行结束的话,在EndAdd方法这里将会阻塞当前线程(这个当前的线程指的是 客户端的线程)35 并等待异步方法(调用服务端的Add方法)的结束,这样往往不能起到多线程并发执行应有的作用。我么真正希望的是在异步执行结束后自动回调设定的操作,这样就可以采用回调的方式36 来实现这样的机制。37 38 去看第二种异步调用的方法39 40 */41 42 43 }44 }45 }
原标题:第五讲:异步操作
关键词:异步
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。