亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

帶翻轉緩沖器的無鎖容器

帶翻轉緩沖器的無鎖容器

夢里花落0921 2022-08-03 12:59:56
對于我的一個必須支持并發讀取和寫入的項目,我需要一個能夠緩沖項的容器,直到使用者一次獲取每個當前緩沖的項。由于生產者應該能夠生成數據,而不管消費者是否讀取當前緩沖區,我提出了一個自定義實現,在的幫助下,將每個條目添加到支持中,直到執行翻轉,這會導致當前條目被返回,同時存儲具有空隊列和元數據的新條目以原子方式存儲在該中。AtomicReferenceConcurrentLinkedQueueAtomicReference我想出了一個解決方案,例如public class FlippingDataContainer<E> {  private final AtomicReference<FlippingDataContainerEntry<E>> dataObj = new AtomicReference<>();  public FlippingDataContainer() {    dataObj.set(new FlippingDataContainerEntry<>(new ConcurrentLinkedQueue<>(), 0, 0, 0));  }  public FlippingDataContainerEntry<E> put(E value) {    if (null != value) {      while (true) {        FlippingDataContainerEntry<E> data = dataObj.get();        FlippingDataContainerEntry<E> updated = FlippingDataContainerEntry.from(data, value);        if (dataObj.compareAndSet(data, updated)) {          return merged;        }      }    }    return null;  }  public FlippingDataContainerEntry<E> flip() {    FlippingDataContainerEntry<E> oldData;    FlippingDataContainerEntry<E> newData = new FlippingDataContainerEntry<>(new ConcurrentLinkedQueue<>(), 0, 0, 0);    while (true) {      oldData = dataObj.get();      if (dataObj.compareAndSet(oldData, newData)) {        return oldData;      }    }  }  public boolean isEmptry() {    return dataObj.get().getQueue().isEmpty();  }}由于其他線程在此線程可以執行更新之前更新值可能會導致可能的重試,因此我需要在每次寫入嘗試時復制實際隊列,否則即使原子引用無法更新,也會將條目添加到共享隊列中。因此,只需將值添加到共享隊列中,就可能導致值條目多次添加到隊列中,而實際上它只應出現一次。雖然與復制隊列的代碼相比,這允許運行測試的速度快10倍以上,但它也經常無法通過消耗測試,因為現在,在消費者線程翻轉隊列并處理數據之后,可能會立即將值元素添加到隊列中,因此并非所有項目似乎都被消耗了。現在的實際問題是,是否可以避免復制后備隊列以獲得性能提升,同時仍然允許使用無鎖算法原子地更新隊列的內容,從而避免中途丟失一些條目?
查看完整描述

2 回答

?
三國紛爭

TA貢獻1804條經驗 獲得超7個贊

首先,讓我們陳述一個顯而易見的事實 - 最好的解決方案是避免編寫任何此類自定義類。也許像java.util.concurrent.LinkedTransferQueue這樣簡單的東西也可以工作,并且不那么容易出錯。如果 a 不起作用,那么 LMAX 干擾器或類似的東西呢?您是否看過現有的解決方案?LinkedTransferQueue

如果你仍然需要/想要一個自定義解決方案,那么我有一個稍微不同的方法的草圖,一個可以避免復制的方法:

這個想法是讓操作圍繞一些原子變量旋轉,試圖設置它。如果線程設法設置了它,那么它將獲得對當前隊列的獨占訪問權限,這意味著它可以追加到它。追加后,它會重置原子變量以允許其他線程追加。它基本上是一個旋轉鎖。這樣,線程之間的爭用就會在追加到隊列之前發生,而不是在追加到隊列之后。put


查看完整回答
反對 回復 2022-08-03
?
MYYA

TA貢獻1868條經驗 獲得超4個贊

我需要在每次寫入嘗試時復制實際隊列

你的想法聽起來像RCU(https://en.wikipedia.org/wiki/Read-copy-update)。Java被垃圾回收通過為您解決解除分配問題使RCU變得更加容易(我認為)。

如果我從你的問題的快速瀏覽中正確理解,你的“讀者”實際上想為自己“聲稱”容器的整個當前內容。這也使它們有效地成為編寫者,但是它們不是讀取+復制,而是可以構造一個空容器,并原子地交換頂級引用以指向該容器。(因此,聲明舊容器進行獨占訪問。

RCU的一大好處是容器數據結構本身不必到處都是原子的。一旦你有對它的引用,就沒有其他人在修改它。


唯一棘手的部分是當作家想要將新內容添加到非空容器中時。然后,您復制現有容器并修改副本,并嘗試將更新后的副本 CAS(比較交換,即)到共享的頂級中。compareAndSet()AtomicReference

一個作家不能只是無條件地交換,因為它可能最終會得到一個非空的容器,無處放它。除非編寫器可以堅持一批工作并旋轉等待讀取器清空隊列...


我在這里假設你的作家有一批工作可以同時排隊;否則RCU對作家來說可能太貴了。很抱歉,如果我錯過了您的問題中的一個細節,可以排除這一點。我不經常使用Java,所以我只是快速編寫這個,以防萬一它有幫助。


查看完整回答
反對 回復 2022-08-03
  • 2 回答
  • 0 關注
  • 86 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號