Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::atomic是如何实现的_C++_Multithreading_Mutex_Lock Free_Stdatomic - Fatal编程技术网

C++ std::atomic是如何实现的

C++ std::atomic是如何实现的,c++,multithreading,mutex,lock-free,stdatomic,C++,Multithreading,Mutex,Lock Free,Stdatomic,我正在研究C++11中的mutex和atomic之间的区别 据我所知,mutex是一种锁机制,它是基于OS/内核实现的。例如,Linux提供了一种机制,即futex。借助于futex,我们可以实现mutex和semaphore。此外,我知道futex是通过低级原子操作实现的,例如CompareAndSet,CompareAndSwap 对于std::atomic,我知道它是基于C++11引入的内存模型实现的。然而,我不知道如何在低级别实现内存模型。如果它也是通过原子操作实现的,比如Compare

我正在研究C++11中的
mutex
atomic
之间的区别

据我所知,
mutex
是一种锁机制,它是基于OS/内核实现的。例如,Linux提供了一种机制,即
futex
。借助于
futex
,我们可以实现
mutex
semaphore
。此外,我知道
futex
是通过低级原子操作实现的,例如
CompareAndSet
CompareAndSwap

对于
std::atomic
,我知道它是基于C++11引入的内存模型实现的。然而,我不知道如何在低级别实现内存模型。如果它也是通过原子操作实现的,比如
CompareAndSet
,那么
std::atomic
互斥体之间有什么区别

总之,如果
std::atomic::is_lock_free
给我一个
false
,那么,我要说
std::atomic
互斥体相同。但如果它给了我一个
true
,那么它是如何在低级别实现的呢

std::atomic
和互斥锁之间有什么区别

互斥锁是一种并发构造,独立于任何用户数据,提供
lock
unlock
方法,允许您保护(强制互斥)代码区域。你可以在那个地区放任何你想要的东西

std::atomic
是T类型的单个实例上的适配器,允许基于每个操作对该对象进行原子访问

互斥体更一般,因为
std::atomic
的一个可能实现是用互斥体保护对底层对象的所有访问

std::atomic
的存在主要是因为另一种常见的实现:使用原子指令2直接执行操作,而不需要互斥。这是当
std::atomic::is\u lock\u free()
返回true时使用的实现。这通常比互斥方法更有效,但仅适用于小到足以由原子指令“一次性”操纵的对象


2在某些情况下,编译器能够使用普通指令(而不是与并发相关的特殊指令),例如正常加载和存储,如果它们在所讨论的平台上提供所需的保证

例如,在x86上,所有
std::atomic
都会加载,以便使用普通加载获得足够小的值,并使用普通存储实现比
memory\u order\u seq\u cst
弱的所有存储
seq_cst
存储是通过特殊指令实现的,但是-10.1之前的GCC上的
mov
后面的
mfence
,以及clang、最近的GCC和其他编译器上的(隐式
lock
xchg mem、reg


还要注意的是,加载和存储之间的不对称是编译器的选择:它们可以将特殊处理放在
seq_cst
加载上,但因为加载的数量通常超过存储,所以在大多数情况下,加载速度较慢。(而且因为快速路径中的廉价加载更有价值。)

如果原子操作是无锁的,则它们的实现方式可能与互斥体组件的实现方式相同。毕竟,要锁定互斥体,您确实需要某种原子操作来确保只有一个线程锁定互斥体

区别在于,无锁的原子操作没有“锁定”状态。让我们比较两种可能的方法来实现变量的原子增量:

首先,互斥方式。我们锁定互斥锁,读写变量,然后解锁互斥锁。如果线程在读增量写过程中被中断,那么尝试执行相同操作的其他线程将阻止锁定互斥锁的尝试。(有关如何在一些实际实现中工作的信息,请参见,对象太大而无法无锁。)

第二,原子方式。CPU“锁定”的只是缓存线,缓存线中包含我们要修改的变量,持续时间为单个读增量写指令。(这意味着CPU延迟响应MESI请求以使缓存线无效或共享缓存线,从而保持独占访问,因此其他CPU无法查看缓存线。MESI缓存一致性始终要求在核心修改缓存线之前独占缓存线的所有权,因此,如果我们已经拥有缓存线,这是便宜的)。我们不可能在指令中被打断。另一个试图访问此变量的线程,最坏的情况是,必须等待缓存一致性硬件确定谁可以修改内存位置

那么我们如何锁定互斥锁呢?我们可能会执行原子比较和交换。因此,轻原子操作是组装重互斥操作的原语


当然,这些都是特定于平台的。但这正是您可能会使用的典型的现代平台所做的。

解释了x86 asm/硬件如何实现原子操作,特别是RMW操作,以及指向更多内容的链接。原子操作是构建互斥体的基础。此外,还有一些涉及到其他ISA,其中硬件原语不是直接支持原子add/sub/CA,但大部分是关于x86的。可能还有其他一些现有的SO答案更普遍地涵盖了其他ISA。它是基于C++11引入的内存模型实现的。-不完全是。编译器必须在硬件内存模型之上实现C++11内存模型语义,而硬件内存模型在C++11之前就已经存在;SMP系统的操作系统和手写asm互斥体需要了解底层内存模型。它们在每个ISA的基础上是不同的。请参阅相关:讨论一些不同的硬件内存模型,在这些模型中,您需要或多或少的障碍来实现acq_rel。杰夫·普雷辛的另一篇文章