使用 “memory_order::relaxed” 或 “memory_order::acq_rel” 生成唯一的标识符?

huangapple go评论57阅读模式
英文:

Using "memory_order::relaxed" or "memory_order::acq_rel" to generate unique IDs?

问题

我在几个地方读到过放松排序(relaxed ordering)可以生成唯一的ID。我对此存疑,因为如果两个线程同时调用:

uniqueId.fetch_add(1, std::memory_order::relaxed);

那么线程A增加的值可能对线程B尚不可见。这意味着两个线程可能获得相同的唯一ID。

因此,我宁愿使用std::memory_order::acq_rel

你怎么看?

在实践中不可能测试。

英文:

I have read in several places that relaxed ordering was ok to generate unique IDs. I have a doubt about that, because if two threads call at the same time:

uniqueId.fetch_add(1, std::memory_order::relaxed);

Then the value incremented by thread-A might not be visible yet to thread-B. This means, both threads could get the same unique ID.

For this reason, I would rather use std::memory_order::acq_rel

What do you think?

Impossible to test, in practice.

答案1

得分: 7

std::memory_order_*关于如何同步存储和加载到除原子对象本身之外的内存位置。

单个原子对象的值始终在所有线程之间保持一致。它有确切的修改顺序,所有线程都同意,并且与每个线程中加载/存储的顺序一致,与std::memory_order_*无关。

(然而,这仅对单独查看的每个原子对象成立。对于多个原子对象则不适用。)

在您的情况下是否std::memory_order::relaxed足够取决于生成的 ID 值是否会在线程之间通过其他共享对象(无论是原子的还是非原子的)使用,但即使在多个线程中使用以下表达式:

uniqueId.fetch_add(1, std::memory_order::relaxed)

只会生成每个 ID 一次(假设uniqueId指的是相同的std::atomic对象,没有其他存储被应用到它,并且没有溢出/循环发生)。不过fetch_add本身是一个原子读-修改-写操作。加载后跟着存储就不是一个原子操作,并且不会保证在加载和存储之间不会有来自其他线程的存储介入。

英文:

std::memory_order_* is about how stores and loads to memory locations other than the atomic object itself synchronize.

A single atomic object's value is always consistent among all threads. It has exactly one modification order that all threads agree on and that is consistent with sequencing of loads/stores in each thread, regardless of std::memory_order_*.

(However this is true only for each atomic object viewed individually. The same does not apply between multiple atomic objects.)

Whether std::memory_order::relaxed is sufficient in your case depends on whether the resulting ID values are going to be used through other shared objects (whether atomic or not) between the threads, but the expression

uniqueId.fetch_add(1, std::memory_order::relaxed)

, even when used in multiple threads will generate each ID only once (assuming uniqueId refers to the same std::atomic object, no other stores are applied to it and no overflow/wrap-around happens). It is important though that fetch_add itself is an atomic read-modify-write. A load followed by a store would not be an atomic operation and wouldn't guarantee that no store from another thread intervenes between the load and store.

huangapple
  • 本文由 发表于 2023年2月19日 23:51:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75501365.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定