<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 線程間共享數據無需競爭 LMAX Disruptor 是一個開源的并發框架,并獲得2011 Duke’s 程序框架創新獎。本文將用圖表的方式為大家介紹Disruptor是什么,用來做什么,以及簡單介紹背后的實現原理。 ### Disruptor是什么? Disruptor 是線程內通信框架,用于線程里共享數據。LMAX 創建Disruptor作為可靠消息架構的一部分并將它設計成一種在不同組件中共享數據非常快的方法。 基于Mechanical Sympathy(對于計算機底層硬件的理解),基本的計算機科學以及領域驅動設計,Disruptor已經發展成為一個幫助開發人員解決很多繁瑣并發編程問題的框架。 很多架構都普遍使用一個隊列共享線程間的數據(即傳送消息)。圖1 展示了一個在不同的階段中通過使用隊列來傳送消息的例子(每個藍色的圈代表一個線程)。 ![](https://box.kancloud.cn/72256c151c85bc531f9c0d6f6ea2b821_238x63.png) 圖 1 這種架構允許生產者線程(圖1中的stage1)在stage2很忙以至于無法立刻處理的時候能夠繼續執行下一步操作,從而提供了解決系統中數據擁堵的方法。這里隊列可以看成是不同線程之間的緩沖。 在這種最簡單的情況下,Disruptor 可以用來代替隊列作為在不同的線程傳遞消息的工具(如圖2所示)。 ![](https://box.kancloud.cn/1d928079c26a9dd2a8219bca5f05d5f4_344x139.png) 圖2 這種數據結構叫著RingBuffer,是用數組實現的。Stage1線程把數據放進RingBuffer,而Stage2線程從RingBuffer中讀取數據。 圖2 中,可以看到RingBuffer中每格中都有序號,并且RingBuffer實時監測值最大(最新)的序號,該序號指向RingBuffer中最后一格。序號會伴隨著越來越多的數據增加進RingBuffer中而增長。 Disruptor的關鍵在于是它的設計目標是在框架內沒有競爭.這是通過遵守single-writer 原則,即只有一塊數據可以寫入一個數據塊中,而達到的。遵循這樣的規則使得Disruptor避免了代價高昂的CAS鎖,這也使得Disruptor非常快。 Disruptor通過使用RingBuffer以及每個事件處理器(EventProcessor)監測各自的序號從而減少了競爭。這樣,事件處理器只能更新自己所獲得的序號。當介紹向RingBuffer讀取和寫入數據時會對這個概念作進一步闡述。 ### 發布到Disruptor 向RingBuffer寫入數據需要通過兩階段提交(two-phase commit)。首先,Stage1線程即發布者必須確定RingBuffer中下一個可以插入的格,如圖3所示。 ![](https://box.kancloud.cn/2f54d74f7f33fdfa4e8d74eae67c1036_304x176.png) 圖 3 RingBuffer持有最近寫入格的序號(圖3中的18格),從而確定下一個插入格的序號。 RingBuffer通過檢查所有事件處理器正在從RingBuffer中讀取的當前序號來判斷下一個插入格是否空閑。 圖4顯示發現了下一個插入格。 ![](https://box.kancloud.cn/c312e1f39ec34f6140a28bdf020e12f7_304x176.png) 圖 4 當發布者得到下一個序號后,它可以獲得該格中的對象,并可以對該對象進行任意操作。你可以把格想象成一個簡單的可以寫入任意值的容器。 同時,在發布者處理19格數據的時候,RingBuffer的序號依然是18,所以其他事件處理器將不會讀到19格中的數據。 圖5表示對象的改動保存進了RingBuffer。 ![](https://box.kancloud.cn/54c67ee5eea2e78647580a038cb6b213_304x176.png) 圖5 最終,發布者最終將數據寫入19格后,通知RingBuffer發布19格的數據。這時,RingBuffer更新序號并且所有從RingBuffer讀數據的事件處理器都可以看到19格中的數據。 ### RingBuffer中數據讀取 Disruptor框架中包含了可以從RingBuffer中讀取數據的BatchEventProcessor,下面將概述它如何工作并著重介紹它的設計。 當發布者向RingBuffer請求下一個空格以便寫入時,一個實際上并不真的從RingBuffer消費事件的事件處理器,將監控它處理的最新的序號并請求它所需要的下一個序號。 圖5顯示事件處理器等待下一個序號。 ![](https://box.kancloud.cn/7c8bec3ca64d7522f73e05266fec453e_344x202.png) 圖6 事件處理器不是直接向RingBuffer請求序號,而是通過SequenceBarrier向RingBuffer請求序號。其中具體實現細節對我們的理解并不重要,但是下面可以看到這樣做的目的很明顯。 如圖6中Stage2所示,事件處理器的最大序號是16.它向SequenceBarrier調用waitFor(17)以獲得17格中的數據。因為沒有數據寫入RingBuffer,Stage2事件處理器掛起等待下一個序號。如果這樣,沒有什么可以處理。但是,如圖6所示的情況,RingBuffer已經被填充到18格,所以waitFor函數將返回18并通知事件處理器,它可以讀取包括直到18格在內的數據,如圖7所示。 ![](https://box.kancloud.cn/74200ec8ed95415f3022adc480bcb921_344x202.png) 圖7 這種方法提供了非常好的批處理功能,可以在BatchEventProcessor源碼中看到。源碼中直接向RingBuffer批量獲取從下一個序號直到最大可以獲得的序號中的數據。 你可以通過實現EventHandler使用批處理功能。在Disruptor性能測試中有關于如何使用批處理的例子,例如FizzBuzzEventHandler。 ### 是低延遲隊列? 當然,Disruptor可以被當作低延遲隊列來使用。我們對于Disruptor之前版本的測試數據顯示了,運行在一個2.2 GHz的英特爾酷睿i7-2720QM處理器上使用Java 1.6.0_25 64位的Ubuntu的11.04三層管道模式架構中,Disruptor比ArrayBlockingQueue快了多少。表1顯示了在管道中的每跳延遲。有關此測試的更多詳細信息,請參閱Disruptor技術文件。 但是不要根據延遲數據得出Disruptor只是一種解決某種特定性能問題的方案,因為它不是。 ### 更酷的東西 一個有意思的事是Disruptor是如何支持系統組件之間的依賴關系,并在線程之間共享數據時不產生競爭。 Disruptor在設計上遵守single-writer 原則從而實現零競爭,即每個數據位只能被一個線程寫入。但是,這不代表你不可以使用多個線程讀數據,而這正是Disruptor所支持的。 Disruptor系統的最初設計是為了支持需要按照特定的順序發生的階段性類似流水線事件,這種需求在企業應用系統開發中并不少見。圖8顯示了標準的3級流水線。 ![](https://box.kancloud.cn/2b4a425f328837990706e24097ddfd01_300x44.png) 圖 8 首先,每個事件都被寫入硬盤(日志)作為日后恢復用。其次,這些事件被復制到備份服務器。只有在這兩個階段后,系統開始業務邏輯處理。 按順序執行上次操作是一個合乎邏輯的方法,但是并不是最有效的方法。日志和復制操作可以同步執行,因為他們互相獨立。但是業務邏輯必須在他們都執行完后才能執行。圖9顯示他們可以并行互不依賴。 ![](https://box.kancloud.cn/1c487f4377670e37250c71260152943b_300x98.png) 圖 9 如果使用Disruptor,前兩個階段(日志和復制)可以直接從RingBuffer中讀取數據。正如圖7種的簡化圖所示,他們都使用一個單一的Sequence Barrier從RingBuffer獲取下一個可用的序號。他們記錄他們使用過的序號,這樣他們知道那些事件已經讀過并可以使用BatchEventProcessor批量獲取事件。 業務邏輯同樣可以從同一個RingBuffer中讀取事件,但是只限于前兩個階段已經處理過事件。這是通過加入第二個SequenceBarrier實現的,用它來監控處理日志的事件處理器和復制的事件處理器,當請求最大可讀的序號時,它返回兩個處理器中較小的序號。 當每個事件處理器都使用SequenceBarrier 來確定哪些事件可以安全的從RingBuffer中讀出,那么就從中讀出這些事件。 ![](https://box.kancloud.cn/1894ad5635dbd25899362989b80135f1_300x98.png) 圖10 有很多事件處理器都可以從RingBuffer中讀取序號,包括日志事件處理器,復制事件處理器等,但是只有一個處理器可以增加序號。這保證了共享數據沒有競爭。 ### 如果有多個發布者? Disruptor也支持多個發布者向RingBuffer寫入。當然,因為這樣的話必然會發生兩個不同的事件處理器寫入同一格的情況,這樣就會產生競爭。Disruptor提供ClaimStrategy的處理方式應對有多個發布者的情況。 ### 結論 在這里,我已經在總體上介紹了Disruptor框架是如何高性能在線程中共享數據,并簡單闡述了它的原理。有關更高級事件處理器以及向RingBuffer申請空間并等待下一個序號等很多策略在這里都沒有涉及,Disruptor是開源的,到代碼中去搜索吧。 注1:源自Oracle出版的Java雜志,http://www.oracle.com/technetwork/cn/java/javamagazine/index.html
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看