Treiber Stack simple analysis

Abstract

Treiber Stack Algorithm is an extension of lock-free stack, use fine-grained concurrency primitives CAS implemented, Treiber Stack in R. Kent Treiber 1986 year in paper Systems Programming: Coping with Parallelism first appeared.

Fundamental

The basic principle of the algorithm is: only if you know the item you want to add the project since the start of the operation the only added at the time, will add new items. This is achieved by the use of comparison and exchange completed. Use the stack when you add a new item, the new item on top of the stack after. Then, the first elements of the new structure (old head) of the second project is compared with the current project. If they match, then you can head into the old new head, otherwise it means that another thread has added another item to the stack, in this case, you have to try again.

When a pop-up item from the stack, before returning to the project, you must check another thread from operating without the addition of other items since the beginning.

Correctness

In some languages, especially those who are not garbage collected language, Treiber stack ABA may face problems. When a process to remove an element from the stack (pop just before the following settings and comparison routines), the stack may be changed by another process, so that the head is the same, but the second element is different. Compare and swap the stack header to stack the old second element, mixing complete data structure. However, since a stronger guarantee Java runtime, so Java version on this page are not affected by this problem (not to be confused objects newly created references refer to the same impossible with any other objects reachable).

Failure such as ABA like testing can be very difficult, because there is a problem of the sequence of events is very rare.

Java example

Here is the Treiber Stack in Java, which is based on Java Concurrency in Practice provided

public class ConcurrentStack<E> {
    private AtomicReference<Node<E>> top = new AtomicReference<>();

    public void push(E item) {
        Node<E> newHead = new Node<>(item);
        Node<E> oldHead;
        do {
            oldHead = top.get();
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead));
    }

    public E pop() {
        Node<E> oldHead;
        Node<E> newHead;
        do {
            oldHead = top.get();
            if (oldHead == null)
                return null;
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead));
        return oldHead.item;
    }

    private static class Node<E> {
        public final E item;
        public Node<E> next;
        public Node(E item) {
            this.item = item;
        }
    }
}

Process Analysis

Operation PUSH


FIG as do the above description, and analyzed workflow.

  1. First single list saved each element in each Stack, the top member variable holds the top element of the stack.
  2. When performing push operation, first create a new elements newHead, and let the new node nextpointer to the topnode (at this time top=oldHead).
  3. Finally replacement CAS top=newHead, CAS exchange conditions top=oldHead.
  4. When the conditions are satisfied, a state after the operation is as follows:

POP operation


FIG as do the above description, and analyzed workflow.

  1. When performing pop operation, creates a new pointer, the pointer to topthe nextelement.
  2. Then replacing CAS top=newHead, CAS exchange conditions top=oldHead.

3. When the conditions are satisfied, a state after the operation is as follows:

Reference: https://en.wikipedia.org/wiki/Treiber_Stack#cite_note-4

Guess you like

Origin blog.csdn.net/demon7552003/article/details/92055256