C++ 11标准是否保证std::atomic<>实现为无锁操作?

22

我现在面临选择一个互斥锁数据结构或无锁(可能是无等待)数据结构的抉择。

进一步调查后,我发现C++11标准是否支持原子类型的无锁操作都没有明确说明,甚至连像atomic_uint32_t这样的基于宽度的整数也不支持。换句话说,不仅std::atomic<>接口不能保证无锁;在整个标准库中唯一可以保证无锁的就是std::atomic_flag

这是真的吗?还是我漏掉了什么?这是什么原因呢?我的意思是,标准将“原子”称为显然不是无锁的东西,并且即使允许在幕后使用互斥锁或阻塞调用。


3
你混淆了“原子性”和“无锁(lock-free)”这两个术语。很明显,原子操作是保证原子性的,否则它们会有一个不同的名称。你说的atomic_flag是唯一保证无锁的。 - Casey
2个回答

12

C++标准并不保证std::atomic<T>操作是无锁的。但是您可以使用std::atomic<T>::is_lock_free()来查看std::atomic<T>的操作是否是无锁的。依据29.6.5 [atomics.types.operations.req]的第7段:

返回:如果对象的操作是无锁的,则为true,否则为false。

如果它不是无锁的,它仍然会执行所需的同步,但是会使用一些锁来实现。


5
根据你的回答,我能否得出结论:"atomic" 的意思是 "在 CPU 级别上不可中断"?如果是这样的话,为什么选择了 "atomic" 这个名称,如果它不能实现其名称的含义呢? - Johannes Schaub - litb
3
原文:@JohannesSchaub: The semantics of the atomic types is that the changes are made in an atomic and suitably thread-safe way. An implementation using licks does so. Another aspect of this approach is that different hardware supports different hardware-level atomic support and the chosen approach allows to implement the best approach possible for a given type.翻译:原子类型的语义是以原子和适当的线程安全方式进行更改。使用锁实现可以做到这一点。该方法的另一个方面是,不同的硬件支持不同的硬件级别的原子支持,所选择的方法允许为给定类型实现最佳方法。 - Dietmar Kühl
2
@JohannesSchaub-litb - 在C++标准中,“原子操作”有三个要求:无数据竞争,缓存一致性和没有跨越原子操作的代码移动。其他上下文可能会使用不同的“原子操作”定义,但是任何此类其他定义都不适用于C++原子操作。 - Pete Becker
3
我有点困惑。我认为使用锁只是实现原子操作的一种方式(相对于使用原子CAS)。即使使用了锁,它仍然是原子的吗?因此标准不能保证 std::atomic 提供无锁实现,但保证提供原子操作是正确的吗? - Bryan Chen
3
遭到了负评。 "Atomic"并不意味着无锁。这些操作是原子的,无论它们使用锁还是原子CPU指令。(在前一种情况下,它们将原子性建立为一个抽象概念,但它们仍然是原子的) - Brennan Vincent
显示剩余2条评论

10
如果您所说的原子操作是指使用硬件支持而无需锁定,则是的,标准不能为此提供保证。为什么?因为不同的体系结构支持不同类型的硬件原子性。std::atomic<> 有一个方便的 is_lock_free() 方法,可用于检查给定对象是否实际上是无锁的,或者内部使用锁来保证原子操作。您可以使用它并在目标硬件上检查它是否是无锁的,然后决定要选择哪种数据结构。
尽管如此,如果目标体系结构对您感兴趣的固定宽度整数具有原子操作的硬件支持,并且您没有从贫民窟中的可疑软件商店中获取标准库副本,那么它可能会使用硬件而不是完整的锁定。

1
我不理解这种方法的含义,有一个名为is_lock_free()的方法并不能解决任何问题,锁定和无锁算法以及数据结构是两个完全不同的世界,具有完全不同的检查和平衡。我无法想象一个C++专家会说“好吧,我们将std::atomic<>描述为“可能是原子性的”,但我们还提供了一种方法来检查它是否是无锁的”,从商业角度来看,这根本没有意义,甚至命名也没有多大意义。 - user2485710
1
一旦意识到您必须支持可能具有非常不同体系结构的平台,这就变得非常合理。 - Markus Koivisto
2
@MarkusKoivisto:这并不是将这两种类型都命名为std::atomic<T>的理由。既然你必须实现两次算法,那么在无锁分支中可以使用atomic<T>,而在需要锁定的分支中可以使用普通的T。真正的问题在于当前接口需要在运行时决定选择哪个算法。它不是constexpr。更糟糕的是,你必须为每个对象检查它,因为它甚至不是static。每个临时对象也必须进行检查。 - MSalters
1
@MSalters:值得一提的是,C++17在atomic<T>中添加了一个名为is_always_lock_freestatic constexpr bool变量,因此如果它不是,则可以使用static_assert - Nicol Bolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接