繁体   English   中英

std::atomic 是如何实现的

[英]How is std::atomic implemented

我正在研究 C++11 中mutexatomic之间的区别。

据我了解, mutex是一种锁机制,是基于操作系统/内核实现的。 例如,Linux 提供了一种机制,即futex futex的帮助下,我们可以实现mutexsemaphore 此外,我知道futex是由低级原子操作实现的,例如CompareAndSetCompareAndSwap

对于std::atomic ,我知道它是基于 C++11 引入的内存模型实现的。 但是,我不知道内存模型在低级别是如何实现的。 如果它也是通过像CompareAndSet这样的原子操作来实现的,那么std::atomicmutex什么区别?

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

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

不同之处在于,无锁的原子操作没有“锁定”状态。 让我们比较两种可能的方法来进行变量的原子增量:

首先,互斥方式。 我们锁定一个互斥锁,我们读-增-写变量,然后解锁互斥锁。 如果线程在读-增-写过程中被中断,则其他尝试执行相同操作的线程将阻止尝试锁定互斥锁。 (请参阅std::atomic 的锁在哪里?了解它在某些实际实现中的工作原理,对于太大而无法 lock_free 的对象。)

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

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

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

std::atomic和 mutex 有什么区别

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

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

std::atomic一种可能实现是使用互斥锁保护对底层对象的所有访问的意义上,互斥锁更通用。

std::atomic存在主要是因为另一个常见的实现:使用原子指令2直接执行操作而不需要互斥锁。 这是当std::atomic<T>::is_lock_free()返回 true 时使用的实现。 这通常比互斥方法更有效,但仅适用于小到可以通过原子指令“一次性”操作的对象。


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

例如,在 x86 上,编译器实现所有std::atomic加载,对于足够小的值使用普通加载,并使用普通存储实现所有比memory_order_seq_cst弱的存储。 seq_cst存储是使用特殊指令实现的 - 在 10.1 之前的 GCC 上mov之后的尾随mfence ,以及(隐式lockxchg mem,reg在 clang、最近的 GCC 和其他编译器上。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM