POSA2读书笔记(二)
呵呵,距离第一篇已经过了半年了……这本书其实翻完已经很久了,但是实践中没有使用到的东西总是一知半解,写出来会误导大众,所以还是不写的好。
今天在为Cindy 3.0a1中并发模型设计头痛的时候,翻了翻这本书中的相关模式,突然有种豁然开朗的感觉:)
Half-Sync/Half-Async:这个模式是从Cindy 1.x一直沿用下来的模式,我估计也是基于NIO的网络应用里面最常用的并发模型。简单的用伪码说:
while (select() > 0) {
getReadyEvents();
deactiveEvents();
addEventsToQueue();
}
网络IO线程不停的select,并把发生的事件添加到某个队列;若干个工作线程并行的从该队列中取的相应的事件,并分发给应用。网络IO线程是串行化的,而工作线程是并行的,所以这个模式就叫半同步/半异步模式。
Leader/Followers:这是今天才豁然开朗的模式:) 在上面的半同步/半异步模式中,瓶颈主要有三处:
-
动态内存分配。网络IO线程把事件添加到队列,工作线程从队列中取事件,不停的有添加删除操作,队列大小在不停的发生变化。
-
多同步操作。在对队列进行添加和删除操作时需要对队列进行同步(或者是在队列内部进行同步),同时各个工作线程之间从队列中取相应事件时需要同步。
-
线程切换:需要不停的在网络IO线程和各工作线程之间做语境切换。比如假设首先所有的工作线程都处于等待状态,这时来了某一个事件,工作线程A被唤醒,去处理该事件;然后又来一个事件,工作线程B被唤醒,去处理该事件;A工作完又回到等待状态,然后又被唤醒……
那么领导/追随模式是怎么样来避免上面的这三个瓶颈呢?
首先领导线程在通过select得到了许多的响应事件,然后领导线程把其中一个从selector上移除,并把一个追随线程提升为领导线程,然后自己就变成了工作线程处理该事件,等到事件处理完成后再变成追随线程或领导线程(如果结束时已经没有可用的领导线程的话)。
这个模式不需要进行动态内存分配,同步操作也比上一个模式少很多,由于领导线程直接变成工作线程处理事件,最大程度的减少了线程切换,所以上面那三 个瓶颈都能够得到很好的解决。但是这个模式也有相应的缺点:实现复杂,不像半同步/半异步模式能非常简单的实现。所谓有得必有失呀:)
这里可以做一个简单的比喻为两种模式做一下总结。场景是把一堆书从A地经过B地搬到C地(我想不到其他好的例子)。
如果是半同步/半异步模型的话,就是有一个人负责把书从A地搬到B地,其余的人在B地等着,一旦B地书来了,就把书再搬到C地。
如果是领导/追随者模型的话,则是所有的人都在A地排队等待,每个人搬一本送到C地,然后回来排到队伍的最后面。(领导/追随者模型并没有规定次序,所以回来后不一定是排在队伍的最后面,反而是非常有可能排在队伍的第二个位置,这样可以提升系统性能)