GCC 原子内置函数和易失性

Kur*_*urt 5 gcc volatile thread-safety atomicreference

我在多线程程序中使用一些全局结构,其中一些成员被多个线程同时修改,另一些则不然。

我没有将任何成员定义为 易失性的,但每当我使用此成员进行读取和写入时,我都会使用原子内置函数,例如 __sync_fetch_and_add。

问题是,我应该定义这个成员还是整个结构体?

我认为由于这个内置函数(锁定前缀),编译器必须访问内存而不是任何寄存器,我是否应该担心其他不会导致竞争条件的成员。

我检查了编译器(gcc 4.6.2)的汇编输出,看来我的假设是正确的。

这是测试代码。

int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}
Run Code Online (Sandbox Code Playgroud)

汇编输出(-O2 -S -masm=intel)

L2:
    add edx, eax
    inc eax
    cmp eax, 2000000000
    jne L2
Run Code Online (Sandbox Code Playgroud)

所以编译器永远不会访问内存(eax = i, edx = sum)

这是第二个测试代码。

volatile int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}
Run Code Online (Sandbox Code Playgroud)

装配输出

L2:
    mov edx, DWORD PTR [esp+28]
    add edx, eax
    mov DWORD PTR [esp+28], edx
    inc eax
    cmp eax, 2000000000
    jne L2
Run Code Online (Sandbox Code Playgroud)

编译器每次都按预期访问内存求和。

最终代码,我的方式。

int sum = 0;

for (i=0; i<2000000000; i++) {
    __sync_fetch_and_add(&sum , i);
}
Run Code Online (Sandbox Code Playgroud)

装配输出。

L2:
    lock add    DWORD PTR [esp+28], eax
    inc eax
    cmp eax, 2000000000
    jne L2
Run Code Online (Sandbox Code Playgroud)

甚至没有像以前那样的临时寄存器(edx),编译器每次都访问内存。

因此,我没有定义任何由多个线程修改或一次仅由一个线程修改的成员。我安全吗?

提前致谢。

Jan*_*dec 4

是的,你很安全。文档没有说它们应该是易失性的,所以它们不应该是易失性的。

*函数__sync酌情充当内存屏障,因此volatile是多余的。而且无论如何它都不允许使用__sync* 函数之外的任何其他内容(只有__sync* 函数生成lock前缀)。

注意:__syncgcc 4.7 中不推荐使用 * 函数,转而使用 C++11 风格的__atomic* 类型,但 gcc 4.6 还没有这些函数。