为什么我的原子操作以意想不到的顺序发生?

时间:2012-02-27 02:15:46

标签: c++ windows multithreading winapi atomic

我不明白为什么会这样。拿下以下的伪代码:

volatile unsigned long count = 0;
volatile unsigned long sum = 0;

void ThreadFunction() {
    InterlockedIncrement(&count);
    InterlockedExchangeAdd(&sum, rand());
}

int main() {
    for (int i = 0; i < 10; ++i) {
        // This is the problematic instruction
        InterlockedExchange(&count, 0);
        InterlockedExchange(&sum, 0);

        std::vector<boost::thread> threads(i);
        for (int j = 0; j < i; ++j)
            threads[j] = boost::thread(ThreadFunction);

        while (count != i)
            Sleep(0);
    }
}

在程序的第一次运行时,i = 0时,主线程sum上的原子交换通常在生成的线程完成后发生。 count上的操作始终以正确的顺序发生。

这只发生过一次;它以正确的顺序为循环的其余部分执行操作。它并不总是发生,但它通常会发生。如果我闯入调试器或在原子添加之前休眠,指令将按正确的顺序执行。

无论哪种方式,结果是线程写入的值被替换为在线程被启动之前应该发生的0。

为什么会这样?为什么我不能依靠处理器完成我按照我给它们的顺序给出的原子指令?原子操作是否意味着存在阻止重新排序的内存障碍?

附注,这是在Visual Studio 2010中发生的。我没有其他版本可以测试。

1 个答案:

答案 0 :(得分:7)

原子操作在某种意义上是原子操作,它是不可中断的。如果没有另一个线程可以查看,它将无法执行或完全完成。

这并不意味着20个原子操作总是以相同的顺序发生,这仍然会受到多线程变幻莫测的影响。

根据线程的启动速度,可能会将其排序为main, thrd1, main, thrd2, main, thrd3, ...main, main, main, main, thrd1, main, main, thrd2, ...或任何其他组合(尽管我非常肯定main始终只是因为{{1}}它的原子操作是在启动任何线程之前的。)