std::atomic
和互斥锁之间有什么区别
互斥锁是一种并发构造,独立于任何用户数据,提供lock
和unlock
方法,可让您保护(强制相互排斥)代码的区域 。您可以在该区域放任何东西。
std::atomic<T>
是类型T 的单个实例上的适配器,允许基于每个操作对该对象进行原子访问。
互斥锁从某种意义上讲是更普遍的,std::atomic
的一种可能的实现是使用互斥锁保护对基础对象的所有访问。
std::atomic
之所以存在,主要是因为 other 常见的实现方式:使用原子指令 2 直接执行操作而无需互斥。这是std::atomic<T>::is_lock_free()
返回true时使用的实现。这通常比互斥锁方法更有效,但仅适用于足够小以通过原子指令“一次性完成”操作的对象。
2 在某些情况下,编译器能够使用 plain 指令(而不是与特殊并发相关的指令),例如正常的加载和存储(如果它们提供了所需的保证)在相关平台上。例如,在x86上,compilers implement的所有负载,甚至seq_cst
都具有普通负载,并使用普通存储实现发布存储。 seq_cst
存储区通过特殊指令实现,但是-mfence
上的尾随clang
和gcc上的lock xchg
。还要注意,加载和存储之间的不对称性是编译器的选择:它们本来可以对seq_cst
加载进行特殊处理,但是因为在大多数情况下,加载通常要比存储慢得多。
,
如果原子操作为lock_free
,则它们的实现方式可能与实现互斥锁的组件相同。毕竟,要锁定一个互斥锁,您确实需要某种原子操作来确保只有一个线程锁定该互斥锁。
不同之处在于,无锁定的原子操作没有“锁定”状态。让我们比较一下两种可能的方法来对变量进行原子递增:
首先,互斥方式。我们锁定一个互斥锁,我们以递增方式写入变量,然后解锁互斥锁。如果线程在读增量写入期间被中断,则其他尝试执行此相同操作的线程将阻止尝试锁定互斥锁。 (请参阅Where is the lock for a std::atomic?,以了解在某些实际实现中该方法如何工作,对于太大而无法锁死的对象。)
第二,原子方式。 CPU仅在一条读-增量-写指令的持续时间内“锁定”包含我们要修改的变量的高速缓存行。 (这意味着CPU会延迟响应使MESI请求无效或共享高速缓存行,从而保持排他访问,因此其他CPU都无法查看它。MESI高速缓存一致性始终要求高速缓存行具有排他所有权,然后内核才能对其进行修改,因此这是便宜(如果我们已经拥有该线路)。我们不可能在指令中被打断。另一个尝试访问此变量的线程,在最坏的情况下,必须等待缓存一致性硬件找出谁可以修改内存位置。
那么我们如何锁定互斥锁?可能我们执行原子比较和交换。因此,轻原子操作是组装重互斥操作的基元。
当然,这都是特定于平台的。但这就是您可能会使用的典型现代平台。
本文链接:https://www.f2er.com/2977942.html