我有两个共享uint64_t变量的线程。第一个线程只是从变量读取而另一个线程只是写入。如果我不使用互斥锁/自旋锁/原子操作等同步它们,是否有可能从写入线程中读取另一个值?读取通过编写线程编写的旧值并不重要。
作为示例,写入线程将变量增加到0到100之间,并且读取线程打印该值。那么,是否有可能在屏幕上看到一个不同于[0-100]范围的值。目前我没有看到任何不同的价值,但我不确定它是否会导致竞争条件。
提前致谢。
答案 0 :(得分:3)
在64位处理器上,数据传输一次是64位,因此您将看到逻辑上一致的值,即您将看不到写入前的32位和写入后的32位。 32位处理器显然不是这样。
您将看到的问题类似于,如果两个线程在不同的核心上运行,则读取线程将不会看到写入线程所做的更改,直到写入线程的核心刷新其缓存。此外,优化可能使得任何一个线程在循环中根本不打算读取存储器。例如,如果您有:
uint64_t x = 0;
void increment()
{
for (int i = 0 ; i < 100 ; ++i)
{
x++;
}
}
编译器可能会生成在循环开始时将x
读入寄存器的代码,并且在循环退出之前不会将其写回内存。你需要像volatile
和记忆障碍这样的东西。
答案 1 :(得分:2)
如果你对这样的变量有竞争条件,那么所有不好的事情都会发生。
现代C的正确工具是原子。只需声明您的变量
uint64_t _Atomic counter;
然后,所有操作(加载,存储,增量......)都将是原子的,即不可分割,不可中断和可线性化。不需要互斥或其他保护机制。
这已在C11中引入,最近的C编译器,例如gcc和clang,现在支持这种开箱即用。