【发布时间】:2019-07-07 10:46:24
【问题描述】:
假设我的代码中有以下全局变量:
std::atomic<uint32_t> x(...);
std::atomic<uint32_t> y(...);
std::atomic<uint32_t> z(...);
我的任务是将 x 和 y 相乘,然后将结果存储在 z 中:
z = x * y
我知道在每个对象上调用 store() 和 load() 的天真方法是完全错误的:
z.store(x.load() * y.load()); // wrong
这样我正在执行三个单独的原子指令:另一个线程可能会滑过并同时更改其中一个值。
我可以选择比较和交换 (CAS) 循环,但它只能保证原子性,仅用于将 z 的旧值与新值 (x*y) 交换:我仍然不确定如何在单个原子步骤中执行整个操作。
我也知道将 x、y 和 z 包装在一个结构中并使其成为原子在这里是不可行的,因为该结构不适合单个 64 位寄存器。编译器会在引擎盖下使用锁(如果我在这里错了,请纠正我)。
这个问题只能用互斥锁解决吗?
【问题讨论】:
-
它不能在汇编级别完成,所以在没有互斥锁的 C++ 级别就不行。
-
也许这是一个更有成果的问题:为什么需要第三个原子变量,它只是对其他两个变量的计算结果?关键是,每当您需要 x 和 y 的乘积时,您可以原子地读取这两个变量并计算它,除非还有其他一些您没有编写的魔法正在发生。
-
@UlrichEckhardt
z,保存结果的变量必须是原子的,因为它是由其他线程写入/读取的。 -
是的。但是它们是 64 位的,所以应该是可行的,对吧?
-
@Ignorant 对于具有更多术语的操作,解决方案是事务性内存。操作是数据库意义上的“事务”,提供 ACID 保证(在大多数情况下)。事务尝试要么提交(在这种情况下,所有操作似乎都是原子的)要么失败(在这种情况下,更改被回滚以保持一致的状态)并且必须重试(如 CAS 循环)。 TM 是一个开放的研究课题。硬件 TM 支持才刚刚成为现实。软件 TM 已经存在了一段时间。 TBH 我不知道它们是如何工作的,但我猜它们使用了很多 CAS?
标签: c++ multithreading concurrency atomic stdatomic