GCC 提供了一组不错的 built-in functions用于原子操作。在 MacOS 或 iOS 上,甚至 Apple 也提供 nice set of atomic functions .然而,所有这些函数都执行一个操作,例如加法/减法、逻辑运算 (AND/OR/XOR) 或比较与设置/比较与交换。我正在寻找的是一种自动分配/读取 int
值的方法,例如:
int a;
/* ... */
a = someVariable;
就是这样。 a
将被另一个线程读取,唯一重要的是 a
具有其旧值或新值。不幸的是,C 标准不保证分配或读取值是原子操作。我记得我曾经在某处读到过,在 GCC 中保证向 int
类型的变量写入或读取值是原子的(无论 int 的大小如何)但我在 GCC 主页上到处搜索并且我再也找不到这个声明了(也许它被删除了)。
我不能使用 sig_atomic_t
,因为 sig_atomic_t 的大小没有保证,而且它的大小也可能与 int
不同。
因为只有一个线程会“写入”一个值到 a
,而两个线程都会“读取”a
的当前值,所以我不需要以原子方式自行执行操作,例如:
/* thread 1 */
someVariable = atomicRead(a);
/* Do something with someVariable, non-atomic, when done */
atomicWrite(a, someVariable);
/* thread 2 */
someVariable = atomicRead(a);
/* Do something with someVariable, but never write to a */
如果两个线程都要写入a
,那么所有操作都必须是原子的,但那样的话,这可能只会浪费 CPU 时间;而且我们项目中的 CPU 资源极少。到目前为止,我们在 a
的读/写操作周围使用互斥量,即使互斥量保持这么短的时间,这已经导致问题(其中一个线程是实时线程并阻塞在互斥锁上会导致它无法满足实时约束,这是非常糟糕的)。
当然我可以使用 __sync_fetch_and_add
来读取变量(并简单地向它添加“0”,不修改它的值)并使用 __sync_val_compare_and_swap
来写入用于编写它(因为我知道它的旧值,所以将其传入将确保始终交换该值),但这不会增加不必要的开销吗?
最佳答案
如果您希望您的负载是原子的并且充当内存屏障,那么带有 0 个参数的 __sync_fetch_and_add
确实是最好的选择。同样,您可以使用带有 0 的 and
或带有 -1 的 or
来使用内存屏障自动存储 0 和 -1。对于编写,如果“获取”屏障足够,则可以使用 __sync_test_and_set
(实际上是一个 xchg 操作),或者如果使用 Clang,则可以使用 __sync_swap
(这是一个 xchg 操作具有完整的屏障)。
但是,在许多情况下,这有点矫枉过正,您可能更愿意手动添加内存屏障。如果你不想要内存屏障,你可以使用 volatile load 来原子地读/写一个对齐的并且不超过一个字的变量:
#define __sync_access(x) (*(volatile __typeof__(x) *) &(x))
(这个宏是一个左值,所以你也可以将它用于存储,如 __sync_store(x) = 0
)。该函数实现与 C++11 memory_order_consume
形式相同的语义,但仅在两个假设下:
您的机器具有一致的缓存;如果不是,您需要在加载之前(或一组加载的第一个之前)内存屏障或全局缓存刷新。
您的机器不是 DEC Alpha。 Alpha 在重新排序内存访问方面具有非常宽松的语义,因此您需要在加载之后(以及一组加载中的每次加载之后)的内存屏障。在 Alpha 上,上述宏仅提供
memory_order_relaxed
语义。顺便说一句,Alpha 的第一个版本甚至无法以原子方式存储一个字节(只能存储一个字,即 8 个字节)。
在任何一种情况下,__sync_fetch_and_add
都可以工作。据我所知,没有其他机器模仿 Alpha,因此这两种假设都不会对当前的计算机造成问题。
关于ios - 原子读/写 int 值 w/o 对 int 值本身的额外操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7149018/