简体   繁体   English

使用 C11 GCC 使数据读/写原子化<stdatomic.h> ?

[英]Making data reads/writes atomic in C11 GCC using <stdatomic.h>?

I have learned from SO threads here and here , among others, that it is not safe to assume that reads/writes of data in multithreaded applications are atomic at the OS/hardware level, and corruption of data may result.我从这里这里的SO 线程中了解到,假设多线程应用程序中的数据读/写在操作系统/硬件级别是原子的是不安全的,并且可能导致数据损坏。 I would like to know the simplest way of making reads and writes of int variables atomic, using the <stdatomic.h> C11 library with the GCC compiler on Linux.我想知道在 Linux 上使用带有 GCC 编译器的<stdatomic.h> C11 库,使读取和写入int变量原子化的最简单方法。

If I currently have an int assignment in a thread: messageBox[i] = 2 , how do I make this assignment atomic?如果我目前在一个线程中有一个int赋值: messageBox[i] = 2 ,我如何使这个赋值原子化? Equally for a reading test, like if (messageBox[i] == 2) .同样用于阅读测试,例如if (messageBox[i] == 2)

For C11 atomics you don't even have to use functions.对于 C11 原子,您甚至不必使用函数。 If your implementation (= compiler) supports atomics you can just add an atomic specifier to a variable declaration and then subsequently all operations on that are atomic:如果您的实现(= 编译器)支持原子,您只需在变量声明中添加一个原子说明符,然后对它的所有操作都是原子的:

_Atomic(int) toto = 65;
...
toto += 2;  // is an atomic read-modify-write operation
...
if (toto == 67) // is an atomic read of toto

Atomics have their price (they need much more computing resources) but as long as you use them scarcely they are the perfect tool to synchronize threads. Atomics 有其价格(它们需要更多的计算资源),但只要您几乎不使用它们,它们就是同步线程的完美工具。

If I currently have an int assignment in a thread: messageBox[i] = 2, how do I make this assignment atomic?如果我目前在一个线程中有一个 int 赋值:messageBox[i] = 2,我如何使这个赋值原子化? Equally for a reading test, like if (messageBox[i] == 2).同样用于阅读测试,例如 if (messageBox[i] == 2)。

You almost never have to do anything.你几乎不需要做任何事情。 In almost every case, the data which your threads share (or communicate with) are protected from concurrent access via such things as mutexes, semaphores and the like.在几乎所有情况下,您的线程共享(或与之通信)的数据都受到保护,不会通过互斥锁、信号量等事物进行并发访问。 The implementation of the base operations ensure the synchronization of memory.基本操作的实现保证了内存的同步。

The reason for these atomics is to help you construct safer race conditions in your code.这些原子的原因是帮助您在代码中构建更安全的竞争条件。 There are a number of hazards with them;它们有许多危险; including:包含:

ai += 7; ai += 7;

would use an atomic protocol if ai were suitably defined.如果适当地定义了ai,将使用原子协议。 Trying to decipher race conditions is not aided by obscuring the implementation.试图破译竞争条件并不能通过模糊实现来帮助。

There is also a highly machine dependent portion to them.他们还有一个高度依赖机器的部分。 The line above, for example, could fail [1] on some platforms, but how is that failure communicated back to the program?例如,上面的行在某些平台上可能会失败[1],但是这种失败是如何反馈给程序的呢? It is not [2].不是[2]。

Only one operation has the option of dealing with failure;只有一种操作可以选择处理失败; atomic_compare_exchange_(weak|strong). atomic_compare_exchange_(弱|强)。 Weak just tries once, and lets the program choose how and whether to retry. Weak 只尝试一次,让程序选择如何以及是否重试。 Strong retries endlessly.强重试无休止。 It isn't enough to just try once -- spurious failures due to interrupts can occur -- but endless retries on a non-spurious failure is no good either.仅仅尝试一次是不够的——由于中断可能会发生虚假故障——但对非虚假故障进行无休止的重试也不好。

Arguably, for robust programs or widely applicable libraries, the only bit of you should use is atomic_compare_exchange_weak().可以说,对于健壮的程序或广泛适用的库,您应该使用的唯一位是 atomic_compare_exchange_weak()。

[1] Load-linked, store-conditional (ll-sc) is a common means for making atomic transactions on asynchronous bus architectures. [1] 加载链接、条件存储 (ll-sc) 是在异步总线架构上进行原子事务的常用方法。 The load-linked sets a little flag on a cache line, which will be cleared if any other bus agent attempts to modify that cache line.加载链接在缓存行上设置一个小标志,如果任何其他总线代理尝试修改该缓存行,该标志将被清除。 Store-conditional stores a value iff the little flag is set in the cache, and clears the flag;如果在缓存中设置了 little 标志,Store-conditional 存储一个值,并清除该标志; iff the flag is cleared, Store-conditional signals an error, so an appropriate retry operation can be attempted.如果标志被清除,Store-conditional 会发出错误信号,因此可以尝试适当的重试操作。 From these two operations, you can construct any atomic operation you like on a completely asynchronous bus architecture.从这两个操作中,您可以在完全异步的总线架构上构建您喜欢的任何原子操作。

ll-sc can have subtle dependencies on the caching attributes of the location. ll-sc 可能对位置的缓存属性有微妙的依赖性。 Permissible cache attributes are platform dependent, as is which operations may be performed between the ll and sc.允许的缓存属性取决于平台,因为可以在 ll 和 sc 之间执行哪些操作。

If you put an ll-sc operation on a poorly cached access, and blindly retry, your program will lock up.如果您对缓存不佳的访问进行 ll-sc 操作,然后盲目重试,您的程序将锁定。 This isn't just speculation;这不仅仅是猜测; I had to debug one of these on an ARMv7-based "safe" system.我不得不在基于 ARMv7 的“安全”系统上调试其中之一。

[2]: [2]:

#include <stdatomic.h>
int f(atomic_int *x) {
    return (*x)++;
}
f:
        dmb     ish
.L2:
        ldrex   r3, [r0]
        adds    r2, r3, #1
        strex   r1, r2, [r0]
        cmp     r1, #0
        bne     .L2       /* note the retry loop */
        dmb     ish
        mov     r0, r3
        bx      lr

that it is not safe to assume that reads/writes of data in multithreaded applications are atomic at the OS/hardware level, and corruption of data may result假设多线程应用程序中的数据读/写在操作系统/硬件级别是原子的是不安全的,并且可能导致数据损坏

Actually non composite operations on types like int are atomic on all reasonable architecture.实际上,像int这样的类型的非复合操作在所有合理的架构上都是原子的。 What you read is simply a hoax.你读到的只是一个骗局。

(An increment is a composite operation: it has a read, a calculation, and a write component. Each component is atomic but the whole composite operation is not .) (增量是一个复合操作:它有一个读、一个计算和一个写组件。每个组件都是原子的,但整个复合操作不是。)

But atomicity at the hardware level isn't the issue.但硬件级别的原子性不是问题。 The high level language you use simply doesn't support that kind of manipulations on regular types.您使用的高级语言根本不支持对常规类型的这种操作。 You need to use atomic types to even have the right to manipulate objects in such a way that the question of atomicity is relevant: when you are potentially modifying an object in use in another thread.您需要使用原子类型甚至有权以原子性问题相关的方式操作对象:当您可能修改另一个线程中正在使用的对象时。

(Or volatile types. But don't use volatile. Use atomics.) (或 volatile 类型。但不要使用 volatile。使用原子。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM