我正在阅读此article,我按照作者的步骤进行操作,但得到了不同的结果。
我创建了两个线程。一个是读者,一个是作家。
// volatile uint64_t variable1 = 0; <- global
// uint64_t* variable2_p = new uint64_t(0); <- in main function
// const unsigned ITERATIONS = 2000000000; <- global
void *reader(void *variable2) {
volatile uint64_t *variable2_p = (uint64_t *)variable2;
// bind this thread to CPU0
unsigned i, failureCount = 0;
for (i=0; i < ITERATIONS; i++) {
uint64_t v2 = *variable2_p;
uint64_t v1 = variable1;
if (v2 > v1) {
failureCount++;
printf("v1:%" PRIu64 ", v2:%" PRIu64 "\n", v1, v2);
}
}
printf("%u failure(s)", failureCount);
return NULL;
}
void *writer(void *variable2) {
volatile uint64_t *variable2_p = (uint64_t *)variable2;
// bind this thread to CPU1
for (;;) {
variable1 = variable1 + 1;
*variable2_p = (*variable2_p) + 1;
}
return NULL;
}
在上面的文章中,作者说比较v2 <= v1
可能会失败一段时间,因为编译器或处理器可能会改变执行顺序。
但是我尝试了很多次,没有任何失败的情况。我很困惑,只有volatile
才使用这种情况吗?或者它会导致一些微妙的错误?
如果不行,请举个例子。非常感谢。
compile command: g++ -O2 -Wall -g -o foo foo.cc -lpthread
uname -a: Linux Wichmann 3.5.0-48-generic #72~precise1-Ubuntu SMP Tue Mar 11 20:09:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
cpuid: Intel(R) Core(TM) i5-3230M CPU @ 2.60GHz
答案 0 :(得分:3)
它可能会失败并不意味着它会在所有机器上失败。如果 例如,你在一个核心上运行,它可能不会 失败。如果你在多核Alpha上运行它几乎 肯定会在某些时候失败。在其他机器上, 结果会有所不同,具体取决于任何数量。
对于volatile
,它不提供多线程保证
码。如果您还使用内联,则可能
汇编程序,或编译器无法理解的其他东西,但是
否则:任何时候你做其他事情是必要的,以确保
线程安全,您不需要volatile
。特别是,如果你
使用C ++ 11原子类型或线程原语volatile
从来没有必要。
答案 1 :(得分:2)
修改的
实际上,在这种非常具体的情况下,代码是正确的,因为variable1
都是volatile
而指针variable2_p
被标记为指向volatile
的指针。这会强制执行内存访问的排序。
挥发性被误用,所以我经常把枪放在这里,抱歉。
旧答案:
使用volatile
只能保证两件事:
volatile
变量的顺序访问不会重新排序你问了一个例子,但你已经在自己的问题中自己做了一遍:
variable1 = variable1 + 1;
*variable2_p = (*variable2_p) + 1;
这可以重新排序,导致另一个线程失败。这在您的特定环境中不会发生是无关紧要的。允许编译器执行此操作,因此代码不正确。