原文地址http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-how-do-i-read-from.html

 

下面部分在了解Disruptor模式系列中,这个模式由LMAX研发。
上一篇文章之后我们都明白了什么是ring buffer和它有多棒。遗憾的是,我还没有讲到使用Disruptor时如何真正地填充或是读取它们。
ConsumerBarrier和COnsumer
我将讲解这些稍稍落后的东西,因为总的来说它应该比较容易理解。假设某些魔法已经将ring buffer填充了。你如何从ring buffer读取东西?


(好的,我开始后悔使用Paint/Gimp了。尽管它是一个购买画图板的好借口,如果我继续坚持下去的话。UML大师们大概也已经在骂我的名字了。)
你的Consumer是那个想要从buffer拿些东西的线程。它访问一个由RingBuffer创建的ConsumerBarrier并且代表Consumer和它交互。正如ring buffer显然需要一个序列号来指明下一个可用槽一样,消费者也需要知道它属于哪个序列号-每个消费者都要能够找出下一个它想要的序列号。那么上面的情形,消费者已经处理了ring buffer中到8为止的所有东西,因此它期望下一个是9。
消费者用下一个序列号作为参数调用ConsumerBarrier中的waitFor方法

final long availableSeq = consumerBarrier.waitFor(nextSequence);

然后ConsumerBarrier返回ring buffer中最大的可用序列号-上面的例子中是12。ConsumerBarrier有一个WaitStrategy用来决定如何等待这个序列号-我暂时不会去详细说明这个,代码中有注释概述它们的优缺点。
然后呢?
所以消费者就一直徘徊等待更多东西被写到ring buffer中,并且被告知写了哪些东西-条目9,10,11和12。现在东西已经在条目中了,消费者可以向ConsumerBarrier要它们了。


Consumer在获取它们的时候更新自己的游标。
你应该开始感觉到这对平滑地延迟高峰多有帮助了-不是提问“我能拿下一个了吗?现在怎么样了?现在呢?”对于每个单独的项,Consumer会简单地说“当你拿到比这个数字大的时候请告诉我”,并且会在返回的时候被告知它还能占有多少条目。因为这些新的条目可以确定已经被写入了(ring buffer的序列已经被更新),而且对这些条目唯一能做的事只有读,不能写,不用锁就可以做到这一点。很好。不但安全和容易编写,因为没有使用锁,它还快很多!
额外好处-你可以用多个Consumer向同一个RingBuffer读取,不需要锁和额外的队列来协调不同的线程。因此,有了Disruptor你可以真正地并行运行你的处理工作。
BatchConsumer是一个消费者代码的例子,如果你实现BatchHandler的话,你可以用BatchConsumer来做我之前讲的高强度地工作。然后处理整批整批条目就很简单了(比如上面例子中9-12),不需要单个取出它们。
修改:注意Disruptor2.0版本使用了和本文中不同的名称。如果你对类名有疑问,请查看我的变更总结

 

部分评论和回复:
Trisha Sep 6, 2011 12:49 AM
你任何时候都应该同时只用一个线程来向一个变量写东西,以避免锁(参考之前发的关于拼接的文章中修改条目那段)。
是的,这是为了避免使用锁,因为锁很伤性能。不过多Consumer/EventHandler的设计也允许你并行地运行东西而不会有资源争夺。

 posted on 2012-02-15 14:43  ﹎敏ō  阅读(1343)  评论(0编辑  收藏  举报