原子变量是否无锁?

时间:2012-04-11 13:30:41

标签: c++ multithreading c++11 x86-64 atomic

当我们谈论原子变量时,例如C ++ 11的atomic<>,它是否可以锁定?或者锁定是不同的东西?如果我使用原子变量管理队列,它会比无锁队列慢吗?

3 个答案:

答案 0 :(得分:36)

标准没有指定原子对象是否无锁。在不为类型T提供无锁原子操作的平台上,atomic<T>对象可以使用互斥锁来实现,该互斥锁不会是无锁的。在这种情况下,在其实现中使用这些对象的任何容器也不会是无锁的。

该标准确实提供了一种检查atomic<T>变量是否无锁的方法:您可以使用var.is_lock_free()atomic_is_lock_free(&var)。对于给定的程序执行,这些函数保证始终为相同类型T返回相同的值。对于int等基本类型,还提供了宏(例如ATOMIC_INT_LOCK_FREE),指定是否可以使用对该类型的无锁原子访问。

答案 1 :(得分:12)

无锁通常适用于多个线程之间共享的数据结构,其中同步机制互斥;目的是所有线程都应该继续取得某种进展,而不是睡在互斥锁上。

atomic<T>变量不使用锁(至少在你的平台上T本身是原子的),但在上面的意义上它们不是无锁的。您可以在无锁容器的实现中使用它们,但它们本身是不够的。

例如,atomic<queue<T>>不会突然使std::queue成为无锁数据结构。但是,您可以实现一个真正无锁的atomic_queue<T>,其成员为atomic

请注意,即使atomic<int>原生为原子而且未在您的平台上使用锁定进行模拟,也不会以任何有趣的方式使其成为无锁。从这个意义上说,普通int 已经无锁:atomic<>包装器可以显式控制内存排序,并可以访问硬件同步原语。

答案 2 :(得分:2)

除了市场营销和冷却因素之外,无论是神奇的C ++语法(棕色)糖是否最终实现直接总线锁定或互斥锁(可能依赖于总线锁定,但作为评论员),它并没有产生两倍的差异注意到,利用操作系统内部功能以更有效的方式完成它,或者如果你不幸在单处理器架构上运行,那就什么也不做。

Mutex已经语义锁定免费。它们在调度程序 - 良好性方面实现了您可能梦寐以求的一切,即优先级反转处理和重入。 您无法在互斥锁上死锁(实际上,如果您非常努力,可能就此而言,但就保护变量而言,您不会这样做),使用互斥锁将不< / strong>对其他进程或操作系统的影响比总线锁定变量更明显。

唯一的区别是程序员可能(通过设计或错误地)持有一个互联网一段不合理的时间,而同样无能的程序员可能会在“无等待”变量上进行轮询并获得相同的愚蠢结果,一个灾难性的公共汽车减速会给整个系统带来更大的压力,而不是使用有缺陷的互斥锁(好吧,我之前对BSOD的暗示只是一个少年的挑战,虽然我仍然怀疑一些司机可能不会对重型公交车的争用非常友好地反应)。无论如何,当互斥锁调用绕过对相当少量内存的线性访问时,这个问题很快就会解决。

“免费锁定”正在向wanabee强硬的程序员推销梦想。我觉得很有趣的是,可能会调用依赖于锁定多处理器总线的机制。

“无锁”变量访问的作用是通过中断总线缓存系统,禁止总线访问调度来破坏硬件,并且通常禁用所有允许平均多处理器总线执行体面工作的机制。
每次你访问“无锁”魔法变量时,你都会将一把沙子扔进公交控制器的齿轮中。

与任何并发访问机制一样,总线锁定变量(对不起,我的意思是“锁定自由变量”)代价高昂且具有extremely difficult to predict or even diagnose的潜在负面影响。

只要您使用这些新的闪亮玩具,就像使用互斥体一样,即非常稀疏,只是出于一些好的理由(而且,没有,代表超酷的先生等待不是其中之一),这很好

但是,如果你开始在整个地方喷洒公交车锁,或者(上帝禁止!)轮询公交车锁定变量,作为正确同步对象的便宜和简单的替代品(超级酷先生等待的是什么) -free可以称之为“通过3个简单步骤酿造自己的自制螺旋锁”),您只需将代码运行的任何尖端硬件转换为大约1995年的Pentium I仿真器。