在这种情况下是需要内存屏障还是只是一个不稳定的

时间:2014-04-29 08:21:26

标签: c++ c multithreading volatile memory-barriers

我正在阅读此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

2 个答案:

答案 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;

这可以重新排序,导致另一个线程失败。这在您的特定环境中不会发生是无关紧要的。允许编译器执行此操作,因此代码不正确。