Yve*_*ves 4 c++ multithreading mutex lock-free stdatomic
我正在研究C++11 中mutex和之间的区别atomic。
据我了解,mutex是一种基于操作系统/内核实现的锁机制。例如,Linux 提供了一种机制,即futex. 在 的帮助下futex,我们可以实现mutex和semaphore。此外,我知道这futex是由低级原子操作实现的,例如CompareAndSet, CompareAndSwap。
对于std::atomic,我知道它是基于C++11引入的内存模型实现的。但是,我不知道内存模型在低级别是如何实现的。如果也是通过原子操作之类CompareAndSet的方式实现的,std::atomic和 和 有mutex什么区别?
总之,如果std::atomic::is_lock_free给我一个false,好吧,我会说这std::atomic与mutex. 但是如果它给了我一个true,它是如何在低级别实现的?
std::atomic和互斥体有什么区别
互斥体是一种并发构造,独立于任何用户数据、产品lock和unlock方法,允许您保护代码区域(在其中强制互斥)。你可以在该区域放置任何你想要的东西。
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 上的尾随,以及 clang、最近的 GCC 和其他编译器上的(隐mfence式)。movlockxchg mem,reg
另请注意,加载和存储之间的不对称是编译器的选择:它们可以对seq_cst加载进行特殊处理,但由于加载通常多于存储,因此在大多数情况下速度较慢。(而且因为快速路径中的廉价负载更有价值。)
如果原子操作是lock_free,则它们的实现方式可能与互斥锁组件的实现方式相同。毕竟,要锁定互斥锁,您确实需要某种原子操作来确保只有一个线程锁定互斥锁。
不同之处在于,无锁的原子操作没有“锁定”状态。让我们比较两种可能的方法来进行变量的原子增量:
首先,互斥方式。我们锁定一个互斥锁,我们读-增-写变量,然后解锁互斥锁。如果线程在读-增-写过程中被中断,则其他尝试执行相同操作的线程将阻止尝试锁定互斥锁。(请参阅std::atomic 的锁在哪里?了解它在某些实际实现中的工作原理,对于太大而无法 lock_free 的对象。)
第二,原子方式。CPU 仅“锁定”包含我们要在单个读-增-写指令期间修改的变量的缓存行。(这意味着 CPU 延迟响应 MESI 请求以使缓存行无效或共享,保持独占访问,因此其他 CPU 无法查看它。MESI 缓存一致性始终需要在内核可以修改缓存行之前独占拥有缓存行,因此这是如果我们已经拥有这条线,那么便宜)。我们不可能在指令中被打断。另一个试图访问这个变量的线程,最坏的情况是,必须等待缓存一致性硬件找出谁可以修改内存位置。
那么我们如何锁定互斥锁呢?可能我们执行原子比较和交换。所以轻原子操作是组装重互斥操作的原语。
当然,这都是特定于平台的。但这就是您可能使用的典型现代平台所做的。
| 归档时间: |
|
| 查看次数: |
3894 次 |
| 最近记录: |