<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Disruptor(無鎖并發框架)-發布 假如你生活在另外一個星球,我們最近開源了一套高性能的基于消息傳遞的開源框架。 下面我給大家介紹一下如何將消息通過Ring buffer在無鎖的情況下進行處理。 在深入介紹之前,可以先快速閱讀一下Trish發表的文章,該文章介紹了ring buffer和其工作原理。 ### 這篇文章的要點如下: 1. ring buffer是由一個大數組組成的。 2. 所有ring buffer的“指針”(也稱為序列或游標)是java long類型的(64位有符號數),指針采用往上計數自增的方式。(不用擔心越界,即使每秒1,000,000條消息,也要消耗300,000年才可以用完)。 3. 對ring buffer中的指針進行按ring buffer的size取模找出數組的下標來定位入口(類似于HashMap的entry)。為了提高性能,我們通常將ring buffer的size大小設置成實際使用的2倍。 這樣我們可以通過位運算(bit-mask )的方式計算出數組的下標。 ### Ring buffer的基礎結構 注意:和代碼中的實際實現,我這里描述的內容是進行了簡化和抽象的。從概念上講,我認為更加方面理解。 ring buffer維護兩個指針,“next”和“cursor”。 ![](https://box.kancloud.cn/16a4fd99e561854fa2bd99719518ae0f_500x226.jpg) 在上面的圖示里,是一個size為7的ring buffer(你應該知道這個手工繪制的圖示的原理),從0-2的坐標位置是填充了數據的。 “next”指針指向第一個未填充數據的區塊。“cursor”指針指向最后一個填充了數據的區塊。在一個空閑的ring bufer中,它們是彼此緊鄰的,如上圖所示。 ### 填充數據(Claiming a slot,獲取區塊) Disruptor API 提供了事務操作的支持。當從ring buffer獲取到區塊,先是往區塊中寫入數據,然后再進行提交的操作。 假設有一個線程負責將字母“D”寫進ring buffer中。將會從ring buffer中獲取一個區塊(slot),這個操作是一個基于CAS的“get-and-increment”操作,將“next”指針進行自增。這樣,當前線程(我們可以叫做線程D)進行了get-and-increment操作后, 指向了位置4,然后返回3。這樣,線程D就獲得了位置3的操作權限。 ![](https://box.kancloud.cn/aacaa867a02226fbb504c9e4117800a9_300x197.jpg) 接著,另一個線程E做類似以上的操作。 ![](https://box.kancloud.cn/4f5ecb0dac2d0fe797b4a3b341d26480_300x233.jpg) ### 提交寫入 以上,線程D和線程E都可以同時線程安全的往各自負責的區塊(或位置,slots)寫入數據。但是,我們可以討論一下線程E先完成任務的場景… 線程E嘗試提交寫入數據。在一個繁忙的循環中有若干的CAS提交操作。線程E持有位置4,它將會做一個CAS的waiting操作,直到 “cursor”變成3,然后將“cursor”變成4。 再次強調,這是一個原子性的操作。因此,現在的ring buffer中,“cursor”現在是2,線程E將會進入長期等待并重試操作,直到 “cursor”變成3。 然后,線程D開始提交。線程E用CAS操作將“cursor”設置為3(線程E持有的區塊位置)當且僅當“cursor”位置是2.“cursor”當前是2,所以CAS操作成功和提交也成功了。 這時候,“cursor”已經更新成3,然后所有和3相關的數據變成可讀。 這是一個關鍵點。知道ring buffer填充了多少 – 即寫了多少數據,那一個序列數寫入最高等等,是游標的一些簡單的功能。“next”指針是為了保證寫入的事務特性。 ![](https://box.kancloud.cn/6e11be78ca4230d7c1040a5d137b0858_300x233.jpg) 最后的疑惑是線程E的寫入可見,線程E一直重試,嘗試將“cursor”從3更新成4,經過線程D操作后已經更新成3,那么下一次重試就可以成功了。 ![](https://box.kancloud.cn/15f6a253b327e73c0885f9639349f4ca_300x233.jpg) ### 總結 寫入數據可見的先后順序是由線程所搶占的位置的先后順序決定的,而不是由它的提交先后決定的。但你可以想象這些線程從網絡層中獲取消息,這是和消息按照時間到達的先后順序是沒什么不同的,而兩個線程競爭獲取一個不同循序的位置。 因此,這是一個簡單而優雅的算法,寫操作是原子的,事務性和無鎖,即使有多個寫入線程。
                  <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>

                              哎呀哎呀视频在线观看