自从我开始多线程以来,我一直在问自己这个问题:
是从不同的线程编写和读取变量未定义的行为吗?
让我们使用最小的例子,我们在一个线程中递增一个整数并读取另一个中的整数。
void thread1()
{
x++;
}
void thread2()
{
if (x == 5)
{
//doSomething
}
}
我理解添加操作不是原子的,因此我可以在第一个线程处于添加操作的中间时从第二个线程读取,但是我不太确定。
x
是否保持其值,直到整个加法操作完成,然后被赋予此新值,或x
是否具有中间状态,从中读取将导致未定义的行为。
如果第一个理论适用,那么在写入时从x
读取只会在添加之前返回该值并且不会出现问题。
如果第二种理论是正确的,那么有人可以更详细地解释添加操作的过程是什么以及为什么它会是未定义的行为(可能有一个例子?)
由于
答案 0 :(得分:0)
评论已经基本正确。
编译器在编译单个函数时可以考虑变量的变化方式。如果函数不能直接或间接地改变某个变量,那么编译器可以假设该变量没有任何变化,除非那里的线程同步。在这种情况下,编译器必须处理另一个线程更改这些变量的可能性。
如果违反了编译器假设(即你有一个bug),那么任何事情都可能发生。这不受限制,因为这会严重限制优化器。你可以假设x
在内存中有一些唯一的地址,但是已知优化器可以移动变量并且有多个变量共享一个地址(只是在不同的时间)。基于单线程假设,这种优化很可能是合理的,这是您的示例违反的假设。您的第二个主题可能会认为它正在查看x
,但也可能会y
。
答案 1 :(得分:-3)
x(32位变量)将始终在32 +位cpu上定义,但不是那么精确。您知道x可以是由++定义的从开始到结束范围的任何值。
类似于下面的情况:x被初始化为0并且你调用了5次thread1,线程2可以看到这个x在0到5的范围内。
这意味着我可以考虑将整数分配给内存为原子。
有两个原因导致两个线程上的x不同步,例如虽然thread1上的x是5,但是thread2可以同时为0。 其中一个原因是cpu cache对于每个核心都是不同的。要同步缓存之间的值,您必须使用内存屏障。您可以使用例如std::atomic为您做得很好