首页 > 解决方案 > memory_order_acquire 对可简单破坏的类型的引用计数指针是否是多余的?

问题描述

这个问题专门针对引用计数指针中的可简单破坏的类型。请参阅Boost 文档中有关原子使用的示例。

减量如下:

if (x->refcount_.fetch_sub(1, boost::memory_order_release) == 1) {
  // A
  boost::atomic_thread_fence(boost::memory_order_acquire);
  delete x;
}
  1. 我们知道,由于memory_order_release,所有的读/写x都在fetch_sub(见这里)之前完成。因此,如果我们碰巧到达了点,A那么所有的使用x都是完整的。

  2. A代码中,标准不能保证我们看到栅栏x之后的最新值......memory_order_acquire

所以这是我关于第二个陈述的问题memory_order_acquire

什么时候x指向一个可简单破坏的类型(例如intwhere xis int * const)是memory_order_acquire没有意义的?我的理由是因为如果x是微不足道的破坏那么最新的更改x不会影响删除x

例如,删除线程是否delete x;看到最新x*x = 10值或过时的值,以便*x = 8销毁过程始终相同(只要指针x本身保持不变)。它知道没有人会x因为发布而从那时起修改它,因此它所要做的就是解除分配。

memory_order_acquire我在这里想念的还有另一个好处吗?我的想法是否正确,如果不是,那么为什么我们需要查看x删除线程上的最新值?

标签: c++c++11memory-barriersstdatomic

解决方案


让我们考虑以下示例:

初始化

int * const x = new int{42};
std::atomic<int> refcount = 2;

线程A和线程B

assert(*x == 42);
if (refcount.fetch_sub(1, std::memory_order_release) == 1) {
  // std::atomic_thread_fence(std::memory_order_acquire);
  delete x;
}

在该示例中,断言可能会失败或更糟,因为它可以访问已经销毁和释放的对象。问题是在一个线程中的访问和另一个线程中的删除之间没有发生关系。即使在使用时*x,断言也可以在同一线程中递减后重新排序。refcountmemory_order_release

为了在关系之前形成这种情况,我们需要线程之间的同步点,而release-acquire正是这样做的。这就是为什么我们需要在删除之前获取栅栏*x。或者,我们可以在递减时使用memory_order_acq_rel而不是,这也足够了。memory_order_releaserefcount


推荐阅读