C++ 为什么';t std::原子初始化是否进行原子释放以便其他线程可以看到初始化的值?
在和的线程清理过程中出现了一些非常奇怪的情况。简而言之,bucket_类型如下所示:C++ 为什么';t std::原子初始化是否进行原子释放以便其他线程可以看到初始化的值?,c++,multithreading,c++11,stl,atomic,C++,Multithreading,C++11,Stl,Atomic,在和的线程清理过程中出现了一些非常奇怪的情况。简而言之,bucket_类型如下所示: struct bucket\u type\u impl { spinlock lock;/=2(如果需要重新加载存储桶列表) 原子计数;//计数用于其中的项目 向量项; bucket_type_impl():计数(0),项(0){ ... 然而线程消除器声称bucket_类型的构造和它的首次使用之间存在竞争,特别是当count atomic从中加载时。结果表明,如果您通过其构造函数初始化std::atomic
struct bucket\u type\u impl
{
spinlock lock;/=2(如果需要重新加载存储桶列表)
原子计数;//计数用于其中的项目
向量项;
bucket_type_impl():计数(0),项(0){
...
然而线程消除器声称bucket_类型的构造和它的首次使用之间存在竞争,特别是当count atomic从中加载时。结果表明,如果您通过其构造函数初始化std::atomic,那么内存位置不会被原子释放,因此其他线程看不到,而h是违反直觉的,因为它是一个原子,并且大多数原子操作默认为memory_order_seq_cst。因此,您必须在构造后显式执行释放存储,以使用其他线程可见的值初始化原子
是否有一些非常紧迫的原因导致具有值消耗构造函数的std::atomic不能使用发布语义初始化自身?如果不是,我认为这是库缺陷
编辑:Jonathan的答案对于历史来说是更好的,但ecatmur的答案链接到Alastair关于该问题的缺陷报告,以及它是如何结束的,只需添加一条注释,说明构造对其他线程不具可见性。因此,我将把答案授予ecatmur。感谢所有回复者,我认为这是正确的很明显,如果需要一个额外的构造函数,那么至少在文档中会突出显示,使用值的构造函数有一些不寻常的地方
<> > >编辑2:< /强>,我最终将此作为C++中的一个缺陷与委员会一起,主持并发部分的Hans Boehm认为这不是一个问题,原因如下:
<> LI> < P> 2014中C++的编译器没有把消耗当作不同的来获取。正如你永远不会在真实世界的代码中那样,把原子传递给另一个线程而不经过一些发布/获取,原子的初始化将被所有原子线程所看到。阅读消毒剂将对此发出警告
这是一个有意的设计选择(标准警告中甚至有一个注释),我认为这是为了与C兼容 设计C++11原子时,WG14也可以将其用于C,使用非成员函数(如
atomic_u load
)和atomic_int
等类型,而不是仅使用C++std::atomic
的成员函数。在原始设计中,atomic_int
类型没有特殊属性原子性只能通过atomic\u load()
和其他函数来实现。在该模型中,atomic\u init
不是一个原子操作,它只是初始化一个POD。只有后续的atomic\u存储(&i,1)
调用才是原子的
最后,WG14决定做一些不同的事情,添加了
\u Atomic
说明符,这使得Atomic\u int
类型具有神奇的属性。我不确定这是否意味着C原子的初始化可以是原子的(目前,C11和C++11中的Atomic\u init
被证明是非原子的),所以可能C++11规则是不必要的。我怀疑人们会争论说,保持初始化非原子化有很好的性能理由,正如interjay上面的评论所说,您需要向另一个线程发送一些通知,该线程构造了obejct并准备从中读取,这样通知就可以引入必要的隔离。为std::atomic
初始化做一次,然后第二次说构造了对象可能是浪费。这是因为转换构造函数是constepr
,并且constepr
函数不能有原子语义之类的副作用
阿拉斯泰尔·梅雷迪斯(Alastair Meredith)在书中写道:
我不确定初始化是否是通过使用constexpr关键字(它限制构造函数的形式)来暗示的,但即使是这样,我认为还是值得明确说明,因为在这种情况下,推理太微妙了
该缺陷的解决方案(由Lawrence Crowl提出)是用注释记录建造商:
[注:构造不是原子的。-结束注]
然后,注释扩展到当前的措辞,给出了一个可能的内存竞争示例(通过通信原子地址的内存\u顺序\u released
操作)
转换构造函数需要是constexpr
的原因是(主要)允许静态初始化。在中,我们看到:
进一步讨论:为什么ctor标记为“constexpr”?Lawrence[Crowl]说这允许静态初始化对象,这很重要,因为否则初始化时会有竞争条件
因此:使构造函数
constexpr
消除静态生存期对象上的争用条件,代价是动态生存期对象中的争用只发生在相当人为的情况下,因为要发生争用,动态生存期原子对象的内存位置必须以这样的方式与另一个线程通信不会导致原子对象的值也同步到该线程。我会说,这是因为构造从来都不是线程通信