C ++ 11:原子变量:lock_free属性:这是什么意思?

时间:2012-09-05 05:52:34

标签: c++ c++11 atomic

我想了解c ++ 11中原子变量的lock_free属性是什么意思。我确实在这个论坛上搜索了其他相关问题但仍然有部分理解。感谢有人能以简单的方式端到端地解释它。

3 个答案:

答案 0 :(得分:7)

如果 无锁,那么谈论会发生什么事情可能最简单。

处理大多数原子任务最明显的方法是锁定。例如,要确保一次只有一个线程写入变量,可以使用互斥锁保护它。任何要写入变量的代码都需要在执行写操作之前获取互斥锁(并在之后释放它)。只有一个线程可以一次拥有互斥锁,因此只要所有线程都遵循协议,在任何给定时间只能有一个线程写入。

但是,如果你不小心,这可能会陷入僵局。例如,假设您需要将两个不同的变量(每个变量都受互斥锁保护)写为原子操作 - 即,您需要确保在写入一个变量时,您还要写入另一个)。在这种情况下,如果你不小心,你可能会导致死锁。例如,让我们调用两个互斥锁A和B.线程1获取互斥锁A,然后尝试获取互斥锁B.同时,线程2获取互斥锁B,然后尝试获取互斥锁A.因为每个互斥锁持有一个互斥锁,两者都不能同时获得,也不能朝着目标前进。

有各种策略可以避免它们(例如,所有线程总是尝试以相同的顺序获取互斥锁,或者在合理的时间段内未能获得互斥锁,每个线程释放它所持有的互斥锁,等待一些随机的时间量,然后再试一次)。

然而,使用无锁编程,我们(显然足够)不使用锁。这意味着上面的死锁根本不会发生。如果操作正确,您可以保证所有线程不断向目标前进。与流行的看法相反,它意味着代码必须比使用锁的编写良好的代码运行得更快。但是,这确实意味着消除了上述的死锁(以及一些其他类型的问题,如活锁和某些类型的竞争条件)。

现在,至于你是如何做到这一点的:答案简短而简单:它变化很大 - 很广泛。在很多情况下,您正在寻找特定的硬件支持,以原子方式执行某些特定操作。您的代码要么直接使用它们,要么扩展它们以提供更高级别的操作,这些操作仍然是原子的并且无锁。在没有硬件支持的情况下实现无锁原子操作甚至可能(尽管只是很少实用)(但鉴于其不切实际,我将继续尝试更详细地介绍它,至少目前如此)。

答案 1 :(得分:4)

Jerry已经提到了锁的常见正确性问题,即它们很难理解并且编程正确。

锁定的另一个危险是你失去了关于执行时间的确定性:如果获得锁定的线程被延迟(例如被操作系统调度或“换出”),则有可能整个程序因为等待锁定而被延迟。相比之下,无锁算法总是保证可以取得一些进展,即使任何数量的线程都被保留在其他地方。

总的来说,无锁编程通常比使用非原子操作的锁定编程慢(有时非常显着),因为原子操作会对缓存和流水线造成重大影响;但是,它提供了确定性和延迟的上限(至少是整个过程的延迟;正如@ J99观察到的那样,只要有足够的其他线程,单个线程可能仍会缺乏资源正在取得进展)。你的程序可能会慢得多,但它永远不会完全锁定并且总能取得一些进展。

硬件架构的本质允许某些小型操作本质上是原子的。实际上,这是支持多任务和多线程的任何硬件的必要条件。在任何同步原语(如互斥锁)的核心,您需要一些类型的原子指令来保证正确的锁定行为。

因此,考虑到这一点,我们现在知道某些类型(如布尔值和机器大小的整数)可以原子地加载,存储和交换。因此,当我们将这样的类型包装到std::atomic模板中时,我们可以预期结果数据类型确实会提供不使用锁的加载,存储和交换操作。相比之下,您的库实现总是被允许实现原子Foo作为由锁保护的普通Foo

要测试原子对象是否无锁,可以使用is_lock_free成员函数。此外,有ATOMIC_*_LOCK_FREE个宏可以告诉您原子原型类型是否可能具有无锁实例化。如果您正在编写想要无锁的并发算法,那么您应该包含一个断言,即您的原子对象确实是无锁的,或者宏上的静态断言具有值2(意味着每个相应类型的对象始终是无锁的。

答案 2 :(得分:0)

无锁是非阻塞技术之一。对于算法,它涉及全局进度属性:只要程序的某个线程处于活动状态,它就可以为其自身或最终为另一个执行其操作的前进步骤。

无锁算法应该在严重争用下具有更好的行为,其中作用于共享资源的线程可能花费大量时间等待其下一个活动时间片。它们在你无法锁定的环境中也几乎是强制性的,比如中断处理程序。

无锁算法的实现几乎总是依赖于比较和交换(有些可能使用ll / sc之类的东西)和策略,其中可见修改可以简化为一个值(主要是指针)变化,使其成为线性化点,如果值已更改,则循环此修改。大多数情况下,这些算法尽可能尝试完成其他线程的作业。一个很好的例子是Micheal& Scott(http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html)的无锁队列。

对于像Compare-and-Swap这样的低级指令,这意味着实现(可能是相应指令的微代码)是等待的(参见http://www.diku.dk/OLD/undervisning/2005f/dat-os/skrifter/lockfree.pdf

为了完整起见,无等待算法强制执行每个线程的进程:每个操作都保证以有限的步数终止。