如何從 Ring Buffer 讀取?


原文地址:http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-how-do-i-read-from.html​​ 作者是 Trisha Gee, LMAX 公司的一位女工程師。

這是理解 LMAX​ 開發的 Disruptor 模式​ 系列博客的下一篇。

上一篇博客​ 我們都明白了什么是 Ring Buffer 以及 它有多棒。遺憾的是,我還沒有提到當你實際使用 Disruptor 時,怎樣讀寫數據。

ConsumerBarrier 與消費者

這里我要稍微反過來介紹,因為總的來說這一段比較容易理解。假設一些魔法已經把數據填入 Ring Buffer 了,怎樣從 Ring Buffer 讀出這些數據?

Disruptor 全解析(2):如何從 Ring Buffer 讀取?

(唔,我開始后悔使用 Paint/Gimp​ 了。盡管這是個購買繪圖板的好借口,如果我繼續寫下去的話... UML 界的權威們大概也在詛咒我的名字了。)

消費者(Consumer)是一個想從 Ring Buffer 里拿出數據的線程,它可以訪問 ConsumerBarrier 對象——這個對象由 RingBuffer 創建並且代表消費者與它互動。就像 Ring Buffer 顯然需要序號才能找到下一個可用節點一樣,消費者一樣需要知道序號——每個消費者都需要找到下一個它要訪問的序號。在上面的例子中,消費者處理完了 Ring Buffer 里序號 8 之前的所有數據,那么它期待訪問的下一個序號是 9。

消費者可以調用 ConsumerBarrier 對象的 waitFor() 方法,傳遞它需要的下一個序號:

Java代碼 復制代碼 收藏代碼

  1. final long availableSeq = consumerBarrier.waitFor(nextSequence);
    final long availableSeq = consumerBarrier.waitFor(nextSequence);

ConsumerBarrier 返回 RingBuffer 的最大可訪問序號——在上面的例子中是 12。ConsumerBarrier 持有一個 WaitStrategy 值來決定它如何等待這個序號,我現在暫時不會描述它的細節,代碼里已經概括了每一種 WaitStrategy 的優點和缺點 。

接下來怎么做?

 

接下來,消費者會一直逛來逛去,等待更多數據被寫入 Ring Buffer。並且,寫入數據后消費者會收到通知——節點 9,10,11 和 12 已寫入。現在序號 12 到了,消費者可以指示 ConsumerBarrier 去拿這些序號里的數據了。

Disruptor 全解析(2):如何從 Ring Buffer 讀取?

拿到了數據后,消費者會更新自己的游標 (cursor)。

你應該已經感覺得到,這樣做是怎樣有助於抹平延遲曲線尖峰了——代替逐個逐個節點的詢問“我能拿下一個數據嗎?現在怎么樣了?現在呢?”,消費者 Consumer 只需要簡單的說“當你拿到的數字比這個要大的時候請告訴我”,函數返回值會告訴它有多少個新的數據節點可以讀取。因為這些新的節點的確已經寫入(Ring Buffer 本身的序號已經更新),而且消費者對這些節點的唯一操作是讀而不是寫,因此訪問不用加鎖。這樣簡直太好了,不僅代碼可以更加安全和簡單,而且不用加鎖的速度超快。

另一個額外的好處是——你可以用多個消費者 Consumer 讀同一個 RingBuffer, 不需要加鎖,也不需要用另外的隊列來協調不同的線程。這樣你可以在 Disruptor 的協調下實現真正的並發數據處理。

BatchConsumer​ 是一個消費端的例子代碼。如果你實現了 BatchHandler​, 你可以用 BatchConsumer 來完成上面我提到的復雜工作。它很容易實現需要成批處理節點(例如上文 9-12 的節點)的功能而不用單獨讀取每一個節點。

更新:注意 Disruptor 2.0 版使用了與本文不一樣的命名。如果你對類名感到困惑,請閱讀我的 變更總結​。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM