我无法找到基于锁的原子和无锁原子之间的语义差异。据我所知,就语言而言,差异在语义上毫无意义,因为语言不能提供任何时间保证。我能找到的唯一保证是内存排序保证,这两种情况看起来都是一样的。
即,除了调用is_lock_free
或atomic_is_lock_free
之外,是否有可能编写一个明确定义的程序,其行为实际上受原子是否无锁影响?
这些功能甚至具有语义含义吗?或者它们只是编写响应式程序的实用黑客,即使语言从未提供过时间保证?
答案 0 :(得分:8)
至少有一个语义差异。
根据C++11 1.9 Program execution /6
:
当通过接收信号中断抽象机器的处理时,在信号处理程序的执行期间未指定既不是类型
volatile std::sig_atomic_t
也不是无锁原子对象的对象的值,并且不属于任何一个对象的任何对象的值 处理程序修改的这两个类别变为未定义。
换句话说,使用这两类变量进行清理是安全的,但应避免对所有其他类别进行访问或修改。
当然,如果您调用未指定/未定义的行为,您可能会认为它不再是定义的程序,但我不完全确定您的意思是格式良好(即可编译)。
但是,即使您忽略了语义差异,性能的差异也值得拥有。如果我必须在线程之间进行通信,我可能会按优先顺序选择:
atomic_flag
(保证无锁)一起控制访问。可以在编译或运行时根据ATOMIC_x_LOCK_FREE
宏选择此行为,这样即使程序行为相同,也会选择该行为的最佳方法。
答案 1 :(得分:5)
在C ++ 11标准中,术语"无锁"在issue LWG #2075中报告的定义不太好。
C ++ 14 Standard定义无锁执行是用C ++语言(N3927批准的)。
引用C ++ 14 1.10 [intro.multithread] /第4段:
定义为无锁(29.7)或表示为无锁(29.4)的原子函数的执行是无锁执行。
- 如果只有一个未阻塞的线程,则该线程中的无锁执行应完成。 [注意:并发执行线程可能会阻止无锁执行的进行。例如,加载锁定的存储条件实现可能会发生这种情况。这个属性有时被称为无阻碍。 - 尾注]
- 当一个或多个无锁执行同时运行时,至少应完成一个。 [注意:某些实现难以为此效果提供绝对保证,因为来自其他线程的重复且特别是不合时宜的干扰可能阻止前向进展,例如,通过在加载锁定和存储条件之间反复窃取高速缓存行以用于不相关的目的说明。实施应该确保这种影响不能无限期地延迟在预期的操作条件下的进展,因此程序员可以安全地忽略这种异常。在此国际标准之外,此酒店有时被称为无锁。 - 尾注]
以上定义"无锁"取决于未阻塞线程的行为。 C ++ Standard没有直接定义 unblocked thread ,但是17.3.3 [defns.blocked]定义了被阻塞的线程:
在可以继续执行之前等待某些条件(除处理器的可用性之外)的线程
(How)原子论的无锁可以影响程序语义吗?
我认为答案是否定的,除了信号处理程序paxdiablo's answer,当"程序语义"意味着原子操作的副作用。 原子的无锁性影响整个多线程程序的进度保证强度。 当两个(或多个)线程同时对同一对象执行无锁原子操作时,这些操作中的至少一个应该在任何最差线程调度下完成。 换句话说,'邪恶'线程调度程序可以在理论上故意阻止基于锁的原子操作的进度。
答案 2 :(得分:3)
Paxdiablo回答得很好,但有些背景可能会有所帮助。
“无锁原子”是一个冗余的术语。最初发明的原子变量点是通过利用硬件保证来避免锁定。但是,每个平台都有自己的局限性,而C ++是高度可移植的。因此,实现必须使用细粒度锁来模拟原子性(通常通过库),原子类型在硬件级别并不存在。
硬件原子与“软件原子”之间的行为差异最小化,因为差异意味着丢失可移植性。另一方面,程序应该能够避免意外使用互斥锁,因此可以通过预处理器可用的ATOMIC_x_LOCK_FREE
进行内省。