假设我的代码中包含以下全局变量:
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位寄存器。编译器会在后台使用锁(如果我错了,请更正我)。
此问题只能通过互斥体解决吗?
答案 0 :(得分:4)
我仍然不确定如何在单个原子步骤中执行整个操作。
仅当您的体系结构支持“ 32位原子乘法”(并且您必须在C ++标准的工具之外进行此操作)或足够宽的原子以执行RMW操作时,才可以这样做在64位上。
我还知道将
x
,y
和z
包装在一个结构中并使其原子化在这里是不可行的,因为该结构不适合单个64位寄存器。
即使它们适合,您仍然需要进行RMW运算,因为无论如何您都不太可能进行原子乘法。
这个问题只能用互斥锁解决吗?
如果您的体系结构支持无锁的64位原子(请检查is_always_lock_free
),则可以将x
和y
放在一起,并根据需要对其进行操作。 / p>
如果变量改为
uint64_t
还是操作像x * y * w * k / j
一样复杂呢?
假设您的体系结构没有128位无锁原子,那么您就不能自动加载那么多数据。请设计您的程序,以使程序不需要原子操作就可以完成(完整)操作,使用锁或寻求避免共享状态的方法。
请注意,即使您将某些操作视为原子操作,也必须意识到,在SMP系统中,无论如何您都会对缓存层次结构施加压力。