ABA problem

多线程及多进程编程同步时可能出现的问题,如果一个值被P1读取两次,两次的值相同,据此判断该值没有被修改过,但该值可能在两次读取之间被P2修改为另外一个value,并在P1再次读取之前修改回了原值。P1被愚弄,认为该值一直没有改变过。

下面的事件序列会导致ABA问题

1.线程P1访问共享内存的value A。

2.P1被抢占,P2开始运行

3.P2读取共享内存的value A,把它变成B,在被抢占之前再次变成A

4. 线程P1再次执行,看到共享内存的A还是原值,继续执行

P1虽然继续执行,但P2隐含的修改可能导致P1的执行结果是错误的。 

  ABA的主要根源是访问了无锁的数据。

用例子来说明

 /* Naive lock-free stack which suffers from ABA problem.*/
  class Stack {
    std::atomic<Obj*> top_ptr;
    //
    // Pops the top object and returns a pointer to it.
    //
    Obj* Pop() {
      while(1) {
        Obj* ret_ptr = top_ptr;
        if (!ret_ptr) return std::nullptr;
        // For simplicity, suppose that we can ensure that this dereference is safe
        // (i.e., that no other thread has popped the stack in the meantime).
        Obj* next_ptr = ret_ptr->next;
        // If the top node is still ret, then assume no one has changed the stack.
        // (That statement is not always true because of the ABA problem)
        // Atomically replace top with next.
        if (top_ptr.compare_exchange_weak(ret_ptr, next_ptr)) {
          return ret_ptr;
        }
        // The stack has changed, start over.
      }
    }
    //
    // Pushes the object specified by obj_ptr to stack.
    //
    void Push(Obj* obj_ptr) {
      while(1) {
        Obj* next_ptr = top_ptr;
        obj_ptr->next = next_ptr;
        // If the top node is still next, then assume no one has changed the stack.
        // (That statement is not always true because of the ABA problem)
        // Atomically replace top with obj.
        if (top_ptr.compare_exchange_weak(next_ptr, obj_ptr)) {
          return;
        }
        // The stack has changed, start over.
      }
    }
  };

栈的初始化序列是A->B->C

线程1开始执行POP

ret=A

next=B

线程1在执行compare_exchange_weak前被挂起

线程2开始执行

{ // Thread 2 runs pop:
    ret = A;
    next = B;
    compare_exchange_weak(A, B)  // Success, top = B
    return A;
  } // Now the stack is top → B → C
  { // Thread 2 runs pop again:
    ret = B;
    next = C;
    compare_exchange_weak(B, C)  // Success, top = C
    return B;
  } // Now the stack is top → C
  delete B;
  { // Thread 2 now pushes A back onto the stack:
    A->next = C;
    compare_exchange_weak(C, A)  // Success, top = A
  }

这是栈是A->C,B已经被delete了。

线程2挂起,线程1继续执行。

compare_exchange_weak(A, B)

检查了A的值还是原值,执行动作,但B已经被释放了,就引发了未定义的行为,这种隐含的错误也非常难于调试。

本文内容引自维基百科

https://en.wikipedia.org/wiki/ABA_problem 

原文地址:https://www.cnblogs.com/learn-my-life/p/5260047.html