无锁队列:为什么读取`Atomic*`两次?

我正在阅读The Art of Multiprocessor Programming, 2ed并且我注意到以下模式用于读取多个Atomic*字段:

while (true) {
    var v1 = atomic1.get();
    var v2 = atomic2.get();
    ...
    var vN = atomicN.get();
    if (v1 == atomic1.get()) {
        // do work
    }
}

这个建设的目的是什么?


我在书中找到的唯一解释是:

... 检查读取的值是否一致 ...

我不明白这个解释。


这是LockFreeQueue,它使用了书中的这种模式:

public class LockFreeQueue<T> {
  
  AtomicReference<Node> head, tail;

  public LockFreeQueue() {
    Node node = new Node(null);
    head = new AtomicReference(node);
    tail = new AtomicReference(node);
  }

  public void enq(T value) {
    Node node = new Node(value);
    while (true) {
      Node last = tail.get();
      Node next = last.next.get();
      if (last == tail.get()) {   // <=== WHY: reading tail.get() again
        if (next == null) {
          if (last.next.compareAndSet(next, node)) {
            tail.compareAndSet(last, node);
            return;
          }
        } else {
          tail.compareAndSet(last, next);
        }
      }
    }
  }

  public T deq() throws EmptyException {
    while (true) {
      Node first = head.get();
      Node last = tail.get();
      Node next = first.next.get();
      if (first == head.get()) {  // <=== WHY: reading head.get() again
        if (first == last) {
          if (next == null) {
            throw new EmptyException();
          }
          tail.compareAndSet(last, next);
        } else {
          T value = next.value;
          if (head.compareAndSet(first, next))
            return value;
        }
      }
    }
  }
}

public class Node {
  
  public T value;
  public AtomicReference<Node> next;

  public Node(T value) {
    this.value = value;
    next = new AtomicReference<Node>(null);
  }
}

我在 SO 上看到了另一个类似的问题:无锁队列算法,重复读取以保持一致性。
但:

  1. 接受的答案有负分,并表示所有答案都可以在不重复阅读的情况下工作,但不提供任何证明
  2. 它讨论了一种不同的算法:该算法显式释放节点,而这本书主要是关于 java 中的算法(其中节点由 GC 隐式释放)。

UPD:这本书说这LockFreeQueue是Maged Michael 和 Michael Scott 的队列算法的稍微简化版本。
这与上面提到的类似 SO 问题中讨论的算法相同。

回答

我认为一般的想法是作者将按照给定的顺序更新字段,并且每次“更新”时第一个字段的值总是会改变。因此,如果读取器在第二次读取时看到第一个字段没有更改,则它知道它已读取所有字段的一组一致的值(快照)。


以上是无锁队列:为什么读取`Atomic*`两次?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>