在不同线程中访问 64 位变量,无需同步或原子性

ava*_*tli 6 multithreading mutex atomic

我有两个线程共享一个 uint64_t 变量。第一个线程只是从变量中读取,而另一个线程只是写入。如果我不使用互斥/自旋锁/原子操作等同步它们,是否有可能从写入的写入线程读取另一个值?读取由写入线程写入的旧值并不重要。

例如,写入线程将变量增加到 0 到 100 之间,读取线程打印该值。那么,是否有可能在屏幕上看到不同于 [0-100] 范围的值。目前我没有看到任何不同的值,但我不确定它会导致竞争条件。

提前致谢。

Jer*_*myP 4

在 64 位处理器上,数据传输一次为 64 位,因此您将看到逻辑上一致的值,即您不会看到写入之前的 32 位和写入之后的 32 位。对于 32 位处理器来说显然不是这样。

您将看到的问题是,如果两个线程在不同的内核上运行,则读取线程将不会看到写入线程所做的更改,直到写入线程的核心刷新其缓存。此外,优化可能会使任一线程根本不必在循环中读取内存。例如,如果您有:

uint64_t x = 0;

void increment()
{
    for (int i = 0 ; i < 100 ; ++i)
    {
        x++;
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器可能会生成x在循环开始时读入寄存器的代码,并且在循环退出之前不会将其写回内存。你需要诸如volatile记忆障碍之类的东西。

  • 此外,即使 CPU 是 64 位并且可以一次性访问变量,但这并不一定意味着访问是原子的。线程 1 可以将变量读入寄存器,线程 2 进来并向该变量写入另一个值,线程 1 根据旧值做出决定。根据规范,这可能根本不重要,也可能是致命的错误。例如,诸如“if(x == 1) print(x)”之类的代码可以打印值 2。 (2认同)
  • @AndrewHenle我本以为编译器会出于性能原因在对齐的边界上分配“uint64_t”,但我可能是错的。 (2认同)