原子操作的危险
Dmitry Vyukov, Sanjay Ghemawat, Mike Burrows, Jeffrey Yasskin, Kostya Serebryany, Hans Boehm, Ashley Hedberg
First written Apr 22, 2014. Updated Jun 23, 2021.
介绍
大多数工程师尝试进行原子操作以尝试生产一些东西无锁机制。此外,程序员喜欢智力难题 使用原子操作。这两者都导致了巧妙的实现,几乎总是不明智的,而且常常是错误的。涉及原子的算法 操作极其微妙。例如,发现一个通用的、高效、无锁、单链表算法经过大量研究和 需要谨慎实施。几乎所有的程序员在执行任务时都会犯错误尝试直接使用原子操作。即使他们 没有犯错误, 结果代码对其他人来说很难维护。
原子操作应该只在少数低级数据结构中使用由几位专家编写,然后经过彻底审查和测试。 不幸的是,许多人尝试编写无锁代码,而这几乎是总是一个错误。请不要陷入这个陷阱:不要使用原子 运营。如果你这样做,你就会犯错误,而这些错误会让业主付出代价。 该代码时间和金钱。
有许多现有的更高级别的组件已经精心制作、审查和测试。如果它们满足您的需要,请使用它们。 否则,请使用互斥体。
注意:该文档以 C++ 为中心,但类似的论点也适用于其他内容 语言也是如此。请参阅 research!rsc了解更多信息 详细讨论硬件、编程语言和 Go 内存模型。
现有组件
在发明自己的并发组件之前先了解常用的并发组件 使用原子解决方案。下面的列表可作为指南,但并不详尽。 C++ 标准库 等库, Abseil 和 Folly 全部 包含相关组件。
- std::shared_ptr and folly's hazard pointer for reference counting
- std::call_once and absl::call_once for one-time initialization
- boost::asio::thread_pool for thread pooling
- absl::Notification for one-time notifications
- std::latch, std::barrier, absl::Barrier, and absl::BlockingCounter for barrier synchronization
- std::future for creating value promises
- Userspace RCU for read-copy-update algorithms and lock-free containers
- thread_local for better locality
- folly's concurrency library for concurrent storage and queues
- folly::TokenBucket for rate limiting
原子操作骗局
原子操作引入了两种不同的危险:
首先,除非您专门使用维持排序的原子操作所有共享内存访问的语义(特别是memory_order_seq_cst”
操作),编译器和处理器都可以并且将会明显地重新排序内存访问
根据 C++ 标准。
在这些情况下,编程规则变得更加复杂,专家通常会仍然很难准确地确定它们。很多人都觉得特别
令人惊讶的是,这种重新排序并不总是停留在传统的同步操作,如互斥锁获取。
如果您确实将自己限制为顺序一致的操作,则可以避免 这个问题,但很可能会发现您的代码现在在 ARM 和 POWER 上运行速度较慢 与使用互斥体相比。 ARM 和 POWER 是弱有序系统,因此 需要特殊的CPU加载指令或内存栅栏来实现 顺序一致性。在像这样的强有序平台上,这不是必需的 x86。
其次,在一个只有