【发布时间】:2013-01-18 12:24:40
【问题描述】:
我在多线程程序中使用了一些全局结构,一些成员被多个线程同时修改,另一些则没有。
我没有定义任何 volatile 成员,但无论何时我将此成员用于读取和写入目的,我都会使用原子内置函数,例如 __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 的循环中使用全局变量并且编译器使用寄存器而不是内存访问,如我所示,线程 2 可以在内存中修改此变量,结果是一团糟。
-
绝对没有! See this answer 进行解释。请注意,GCC 的 _sync 函数本身就是一个屏障。
-
我认为你没有理解我之前的评论。阅读 Andrei Alexandrescu 撰写的这篇文章。 drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/… 这样说,尽管 C 和 C++ 标准在线程方面都明显保持沉默,但它们确实以 volatile 关键字的形式对多线程做出了一些让步。
-
C11 有很多新的线程特性。在这个问题上绝对不是沉默!
标签: gcc thread-safety volatile atomicreference