GCC原子内置和易变

时间:2013-02-03 18:48:56

标签: gcc thread-safety volatile atomicreference

我在多线程程序中使用了一些全局结构,一些成员同时被多个线程修改,而另一些则没有。

我没有定义任何这个成员易失性,但是无论何时我使用这些成员进行读写,我都使用原子内置函数,例如__sync_fetch_and_add。

问题是,我应该定义这个成员还是整个struct volatile?

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

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

这是测试代码。

int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}

装配输出(-O2 -S -masm = intel)

L2:
    add edx, eax
    inc eax
    cmp eax, 2000000000
    jne L2

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

这是第二个测试代码。

volatile int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}

装配输出

L2:
    mov edx, DWORD PTR [esp+28]
    add edx, eax
    mov DWORD PTR [esp+28], edx
    inc eax
    cmp eax, 2000000000
    jne L2

编译器每次按预期访问内存以获得总和。

最终的代码,我的方式。

int sum = 0;

for (i=0; i<2000000000; i++) {
    __sync_fetch_and_add(&sum , i);
}

装配输出。

L2:
    lock add    DWORD PTR [esp+28], eax
    inc eax
    cmp eax, 2000000000
    jne L2

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

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

提前致谢。

1 个答案:

答案 0 :(得分:2)

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

__sync *函数适当地充当内存屏障,因此volatile将是多余的。并且它不允许使用__sync *函数之外的任何其他内容(只有__sync *函数生成lock前缀)。

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