使用 .NET 实现 Ajax 长连接 (Part 2 - Mutex Wait & Signal)


======================================================
注:本文源代码点此下载
======================================================

在上一次的文章中,我们说到了如何设计一个asp.net web service来处理长连接请求。很多人对此就提出了问题,如何hold住请求让它30秒不断开了?这其实很简单,只需要sleep()一下就可以了:

thread.sleep(30 * 1000);

然而问题是,我们不是要等30秒然后看看是否有事件需要返回,而是在这30秒内随时有事件随时返回。因此,我们需要一套机制来在等待的过程中检查是否有事件发生了。

monitor模型

在.net里面,大家最熟悉的线程同步模型应该就是monitor模型了。没听说过?就是c#的那个lock关键字,实际上它编译出来就是一对monitor.enter()和monitor.exit()。

通过lock命令,我们可以针对一个对象创建一个临界区,代码执行到临界区入口时必须获取到该对象的锁才能执行下去,并且在临界区的出口释放该锁。然而这种模型不太适用于解决我们的问题,因为我们需要等待一个事件,如果使用lock来等待的话,那就是说要先在web service外部把对象锁上,然后等事件触发了就解锁,这时候web service才顺利进入临界区域。

事实上,要进行这类型的阻塞,还有一个更好的选择,那就是mutex。

mutex模型

mutex,也就是mutual exclusive的缩写,“互斥”的意思。mutex是如何运作的?这有点像是银行的排队叫号系统,所有等待服务的人都坐在大厅里等候(wait)被叫,当一个服务窗口空闲时它就会发出一个信号(signal)来通知下一位等候服务的人。总之,所有执行wait指令的线程都在等候,而每一个signal能够让一个线程结束等候继续执行。

在.net里面,wait和signal这两个操作分别对应mutex.waitone()和mutex.releasemutex()这两个方法。我们可以让web service的线程使用mutex.waitone()进入等候状态,而在事件发生时使用mutex.releasemutex()来通知web service线程。因为必须在mutex.releasemutex()发生后mutex.waitone()才可能继续执行下去,因此能够执行下去就证明必然有事件发生了并且调用了mutex.releasemutext(),这时候就可以放心地去读取事件消息了。

简单示例

在选定使用mutex模型后,我们来编写一个简单的示例。首先,我们要在webservice派生类内定义一个mutex,还有一个代表消息的字符串。

mutex mutex = new mutex();

string message;

然后,我们定义两个webmethod。为了把问题简单化,我们选用上一篇文章中开头所说的两个函数签名,也就说只能在一个web service内自己发自己收,没有发送目标的概念,也没有超时的概念,还没有可靠性设计。同时,我们将message类型替换为普通字符串,以便于我们测试。

我们先编写发送消息的函数:

public void send(string message) {

this.message = message;

this.mutex.releasemutex();

}

在这个发送函数里,首先我们把消息放进了类内全局的变量中,然后让全局的mutex类释放一个signal。这时候,如果有线程在等待,它可以马上执行下去。如果此时没有线程在等待,那么下一个wait的线程执行到该阻塞的地方就能够不受阻塞继续执行下去。

现在我们来编写接收消息的函数:

public string wait() {

this.mutex.waitone();

return this.message;

}

接收函数一开始就进入wait状态。在得到signal后,需要做的事情就是把全局的消息返回给客户端。

亲身体验

最后,我们可以通过asp.net web service本身支持的web测试界面来测试一下我们的代码。我们开两个浏览器窗口,一个进入send()调用,一个进入wait()调用。然后我们按照如下方法来测试:

首先执行send("hello"),然后执行wait()。这时候你可以马上看到"hello"。

首先执行wait(),让它等待返回,这时候执行send("hello")。随后你可以看到wait()那段返回"hello"了。

按如下顺序执行:send("hello");wait();send("world");wait();

按如下顺序执行:send("hello");send("world");wait();wait();

按如下顺序执行:wait();wait();send("hello");send("world");

按如下顺序执行:wait();send("hello");wait();send("world");

你会发现这样一些奇怪的结果:第3个测试返回的是"world"和"world"。第5个测试先返回"hello"的并不一定是先执行的那个wait()线程。后者在某些情况下不是什么问题,特别是长连接中一般之后一个wait()线程在等待中,所以我们可以不管。而前者,则是因为没有消息队列所造成的,我们只有长度为1的消息窗口,所以只能缓存最后一个消息。这个问题我们将在下一篇文章中解决。

小结

在本文中,我们看到了不同的线程同步模型的差异。monitor模型的lock本质上是一个semaphore,也就是一个不能连续signal的mutex,一个signal发出去后必须被一个wait接收了才能进行下一次的signal。同时,semaphore也限制了signal和wait必须在同一个线程内成对执行,而mutex则没有此限制。虽然.net是针对monitor模型优化的,但在我们的需求当中,只能通过mutex模型来解决。

接着,我们便写了一个小小的消协发送与接收函数,实现了我们想要的阻塞式web service。同时我们也看到了没有消息队列造成的问题,因此确定接下来我们要做一个消息队列。如果你想知道消息队列如何编写,欢迎订阅我的blog:

cat in chinese (feed)

cat in dotnet (feed)


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值