c++ - 指向普通可破坏类型的引用计数指针上的 memory_order_acquire 是多余的吗?

标签 c++ c++11 memory-barriers stdatomic

这个问题具体是关于引用计数指针中的普通可破坏类型。请参阅 Boost's documentation 中的示例关于原子的使用。

递减如下:

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_releasex 的所有读/写在 fetch_sub 之前完成(参见here)。因此,如果我们碰巧到达点 A那么 x 的所有用途已完成。

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

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

何时 x指向普通可破坏类型(例如 int ,其中 xint * const )是 memory_order_acquire无意义?我的理由是因为如果 x是微不足道的可破坏然后最新的变化 x不影响 x 的删除?

例如删除线程的delete x;是否查看最新x这样*x = 10或过时的值,例如 *x = 8无论如何,销毁过程总是相同的(只要指针 x 本身保持不变)。它知道没有人会修改 x从那时起,感谢发布,因此它所要做的就是解除分配。

memory_order_acquire还有其他好处吗?我在这里失踪了? 我的想法是否正确?如果不正确,为什么我们需要查看 x 的最新值?在删除线程上?

最佳答案

该标准没有是根据竞争线程中可能发生的操作交错来编写的。我认为这样的规范过于严格:编译器需要重新排序加载和存储,甚至跨同步点,以提高速度。

相反,该标准只是简单地说明了什么是数据竞争,以及它们是未定义的行为。

非正式地,a data race occurs when :

  • 一个线程访问内存;
  • 另一个线程写入同一内​​存位置;和
  • 两个线程之间没有同步来强加 两次访问的顺序。

即使析构函数很简单,删除对象也算作写入。如果两个线程访问 *x,然后使用您的代码对其进行 decref,并且一个线程删除 x,我们可以看到所有三个要求都已满足。 *x 上存在数据争用。

数据竞争不在 x->refcount_ 上,因为两个线程都通过原子操作访问它(在标准中,这是对“数据竞争”的草率定义的显式异常(exception))上面给出)。但由于两个线程上的内存排序都是release,因此它不会同步这两个线程。

<小时/>

人们经常尝试想象编译器的恶作剧可能会在实践中导致实际的不当行为,看看竞争是否可以被认为是“良性的”,但我已经放弃了这一点。根据标准,缺乏释放-获取切换会导致行为未定义。

关于c++ - 指向普通可破坏类型的引用计数指针上的 memory_order_acquire 是多余的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58774326/

相关文章:

c++ - ALSA 捕获丢失的帧

c++ - "class has no constructors"类模板错误

c++ - 将构造函数声明为私有(private)会显示错误。至少有一个公共(public)构造函数是强制性的吗?

c++ - std::function 的模板参数中 `const` 修饰符和引用的作用是什么

c++ - 互斥锁映射c++11

multithreading - “full memory barrier”的反面是什么?

multithreading - 英特尔 SFENCE 有发布语义吗?

c++ - x86 内存排序测试显示英特尔手册中规定不应该进行重新排序?

c++ - 用于服务器-客户端配置管理的 API 或库

c++ - 结构学生记录程序