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