Boost提供了一个原子引用计数共享指针的示例
以下是相关的代码段以及所使用的各种排序的说明:
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)
使用memory_order_relaxed可以始终增加引用计数器:对象的新引用只能从现有引用形成,并且将现有引用从一个线程传递到另一个线程必须已经提供任何所需的同步.
在删除不同线程中的对象之前,必须在一个线程(通过现有引用)中强制执行对对象的任何可能访问.这是通过在删除引用之后的"释放"操作(通过此引用对对象的任何访问必须明显发生之前)和在删除对象之前的"获取"操作来实现的.
可以将memory_order_acq_rel用于fetch_sub操作,但是当引用计数器尚未达到零时,这会导致不需要的"获取"操作,并且可能会造成性能损失.
我无法理解为什么memory_order_acquire屏障在之前是必要的delete x手术.具体来说,如何在不违反单线程语义的情况下对编译器/处理器重新排序和测试值delete x之前的内存操作是否安全?fetch_subx == 1
编辑我想,我的问题不是很清楚.这是一个改写版本:
读取x(x->refcount_.fetch_sub(1, boost::memory_order_release) == 1)和delete x操作之间的控制依赖性是否会提供任何排序保证?即使考虑单线程程序,编译器/处理器是否可以重新排序与delete x之前的操作相对应的指令fetch_sub …