原子参考计数

Sil*_*ler 17 c++ multithreading atomic reference-counting c++11

我试图准确理解线程安全的原子引用计数是如何工作的,例如std::shared_ptr.我的意思是,基本概念很简单,但我真的很担心减法加上如何delete避免竞争条件.

Boost的这个教程演示了如何使用Boost原子库(或C++ 11原子库)实现原子线程安全的引用计数系统.

#include <boost/intrusive_ptr.hpp>
#include <boost/atomic.hpp>

class X {
public:
  typedef boost::intrusive_ptr<X> pointer;
  X() : refcount_(0) {}

private:
  mutable boost::atomic<int> refcount_;
  friend void intrusive_ptr_add_ref(const X * x)
  {
    x->refcount_.fetch_add(1, boost::memory_order_relaxed);
  }
  friend void intrusive_ptr_release(const X * x)
  {
    if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
      boost::atomic_thread_fence(boost::memory_order_acquire);
      delete x;
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

好的,所以我得到了一般的想法.但我不明白为什么以下情况不可能:

说refcount是当前的1.

  1. 线程A:原子地将refcount递减为0.
  2. 线程B:原子地将refcount递增到1.
  3. 线程A:调用delete托管对象指针.
  4. 线程B:将refcount视为1,访问托管对象指针... SEGFAULT!

我无法理解是什么阻止了这种情况发生,因为在refcount达到0和删除对象之间没有任何阻止数据竞争的东西.减少引用计数和调用delete是两个独立的非原子操作.那么没有锁定怎么可能呢?

Mik*_*eMB 22

您可能高估了shared_ptr提供的线程安全性.

原子引用计数的本质是确保如果访问/修改a的两个不同实例shared_ptr(管理同一对象),则不存在竞争条件.但是,shared_ptr如果两个线程访问同一个shared_ptr对象(其中一个是写入),则不能确保线程安全.一个例子是,例如,如果一个线程解引用指针,而另一个线程重置它.
因此shared_ptr,只要在shared_ptr的单个实例上没有竞争(它也不会访问指向线程安全的对象),唯一保证就是不会有双重删除和泄漏.

因此,创建shared_ptr的副本只是无竞争,如果没有其他线程,可以在逻辑上同时删除/重置它(你也可以说,它不是内部同步的).这是您描述的场景.

要再次重复:从多个线程访问单个 shared_ptr 实例, 其中一个访问是写入(指针)仍然是竞争条件.

如果您想以std::shared_ptr线程安全方式复制,则必须确保所有加载和存储都通过std::atomic_...专门的操作发生shared_ptr.

  • “shared_ptr”上的原子操作鲜为人知且被低估。+1 (2认同)

Nat*_*ica 5

这种情况永远不会出现.如果共享指针的引用计数达到0,则删除对它的最后一个引用,并且删除指针是安全的.无法创建对共享指针的另一个引用,因为没有可以复制的实例.