当类型不是 Integral 时,如何使用 std::atomic 执行基本操作?
Posted
技术标签:
【中文标题】当类型不是 Integral 时,如何使用 std::atomic 执行基本操作?【英文标题】:How to perform basic operations with std::atomic when the type is not Integral? 【发布时间】:2014-05-31 17:30:31 【问题描述】:确切地说,我只需要将一个 double 增加另一个 double 并希望它是线程安全的。我不想为此使用互斥锁,因为执行速度会大大降低。
【问题讨论】:
相关:Atomic double floating point or SSE/AVX vector load/store on x86_64。基本上与此相同的答案,但具有 x86 asm 详细信息。 (不幸的是,有些编译器在将数据从 XMM 转换为compare_exchange
的整数甚至使用 atomic<double>
加载/存储时效率相当低。)
【参考方案1】:
所以使用积分原子作为内存屏障。这是一个带有来源和解释的页面:http://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/
【讨论】:
内存屏障只有助于排序,而不是原子性。这就是为什么您需要一个 CAS(或 LL/SC)来使整个读-修改-写原子化。【参考方案2】:通常,C++ 标准库试图只提供可以有效实现的操作。对于std::atomic
,这意味着可以在“通用”架构上的一条或两条指令中无锁执行的操作。 “通用”架构具有用于整数的原子获取和添加指令,但不适用于浮点类型。
如果您想为原子浮点类型实现数学运算,您必须自己使用 CAS(比较和交换)循环 (Live at Coliru):
std::atomic<double> foo0;
void add_to_foo(double bar)
auto current = foo.load();
while (!foo.compare_exchange_weak(current, current + bar))
;
【讨论】:
谢谢你的回答,我没有意识到为了做到这一点(安全地添加两个双线程)我需要这么脏(或者至少在我看来是这样)的解决方法。 我有点困惑。如果 foo != current 由于 foo 被负载和 CAS 之间的另一个线程修改,为什么这段代码不会进入无限循环? @Joecompare_exchange_weak
通过引用获取其第一个参数,并在失败时将其更新为观察值。因此,如果 CAS 因foo != current
而失败,则循环会从更新的current
中计算出一个新的current + bar
并重试。
@Casey 感谢您的澄清。以上是关于当类型不是 Integral 时,如何使用 std::atomic 执行基本操作?的主要内容,如果未能解决你的问题,请参考以下文章
使用类作为数据类型时如何在 std::variant 中存储值?