C++ 当另一个线程正在写入一个整数时,读取该整数是否安全?

C++ 当另一个线程正在写入一个整数时,读取该整数是否安全?,c++,multithreading,C++,Multithreading,我遇到过这样一种情况,很多不同的线程写入和读取一些数据,互斥的开销真的会降低性能,所以我尽量减少它们的使用和持续时间 关于这一点: 公认的答案是,在可能被覆盖的情况下读取数据可能会产生损坏的结果。但它并没有指定数据量。我想象一个大数组读取与单个整数读取是不同的,因为cpu不会逐位写入整数,对吗 因此,从这个角度来看,读取整数(为x64编译时为64位1)是否安全?从形式和语言的角度来看,行为是未定义的,因此不,它是不安全的 从实用的角度来看,它可能会,因为int应该是CPU原子类型(即读或写是单个

我遇到过这样一种情况,很多不同的线程写入和读取一些数据,互斥的开销真的会降低性能,所以我尽量减少它们的使用和持续时间

关于这一点:

公认的答案是,在可能被覆盖的情况下读取数据可能会产生损坏的结果。但它并没有指定数据量。我想象一个大数组读取与单个整数读取是不同的,因为cpu不会逐位写入整数,对吗


因此,从这个角度来看,读取整数(为x64编译时为64位1)是否安全?

从形式和语言的角度来看,行为是未定义的,因此不,它是不安全的

从实用的角度来看,它可能会,因为
int
应该是CPU原子类型(即读或写是单个指令,不能并行访问内存)


只需使用
std::atomic
并(安全地)使用它。

从形式和语言的角度来看,行为是未定义的,因此不,它是不安全的

从实用的角度来看,它可能会,因为
int
应该是CPU原子类型(即读或写是单个指令,不能并行访问内存)


只需使用
std::atomic
并(安全地)使用它。

正式地说,这是未定义的行为,因此编译器可以决定不执行“合理”的操作。即使使用“友好”的编译器,它也取决于您所称的“安全”以及体系结构。我们还假设您是在64位对齐的地址上写的,该地址必须与某个单页缓存对齐

如果没有适当的同步,一个线程的更新可能对以后读取该值的其他线程根本看不到,或者在一段时间内看不到,或者他们甚至可能看到一些中间值(例如,如果编译器决定通过将值读入CPU寄存器并将其添加回内存位置两次来完成乘以3的操作)。读取过时值的线程可能会从中更新(例如,添加一个数字),从而破坏其他线程的更新

不过,在实践中,如果您只有一个更新线程,并且不关心在任何特定时间段内其他线程是否可以看到更新,那么这可能是可以的(例如,您正在更新线程的设置标志,表示Control-C已按下,应用程序可以在下一个方便的时刻关闭,但不管是在1毫秒还是2秒后关闭),您很可能没有问题


否则,请使用原子或一些锁定、线程本地存储或任何您能想到的其他安全优化。

正式地说,这是未定义的行为,因此编译器可以决定不执行“合理”的操作。即使使用“友好”编译器,也取决于您称之为“安全”的内容,以及体系结构。我们还假设您是在64位对齐的地址上编写的,该地址必须与某个单页缓存对齐

如果没有适当的同步,一个线程的更新可能对以后读取该值的其他线程根本看不到,或者在一段时间内看不到,或者他们甚至可能看到一些中间值(例如,如果编译器决定通过将值读入CPU寄存器并将其添加回内存位置两次来完成乘以3的操作)。读取过时值的线程可能会从中更新(例如,添加一个数字),从而破坏其他线程的更新

不过,在实践中,如果您只有一个更新线程,并且不关心在任何特定时间段内其他线程是否可以看到更新,那么这可能是可以的(例如,您正在更新线程的设置标志,表示Control-C已按下,应用程序可以在下一个方便的时刻关闭,但不管是在1毫秒还是2秒后关闭),您很可能没有问题


否则,使用原子或一些锁定、线程本地存储或任何其他可以想到的安全优化。

< P>简而言之:不,这是UB的C++标准,你应该遵守这个规则。 更详细的解释:在x86中,对不大于字大小的正确对齐的内存位置的读写是原子的。但是,如果没有同步,编译器可以以不明显的方式优化代码:缓存值、重新排序读取等。在这种情况下,您可能会发现一个线程的更改在另一个线程中根本不可见。其他有趣的是如果你期望某件事以特定的顺序发生,那么可能会发生意外


使用<原子> <代码>类型。它面向性能,甚至可以在支持它的平台上自由地锁。

简而言之:不,这是UB的C++标准,你应该遵守这个规则。

更详细的解释:在x86中,对不大于字大小的正确对齐的内存位置的读写是原子的。但是,如果没有同步,编译器可以以不明显的方式优化代码:缓存值、重新排序读取等。在这种情况下,您可能会发现一个线程的更改在另一个线程中根本不可见。其他有趣的是如果你期望某件事以特定的顺序发生,那么可能会发生意外


使用
原子
类型。它面向性能,甚至可能在支持它的平台上是无锁的。

这不仅仅是阅读“腐败”更新,是关于它们的顺序和可见性。您可以在一个线程中设置一个值,而在另一个线程中永远看不到它。您可以按顺序设置两个变量,然后在另一个线程中按相反的顺序更改。您可以在一个线程中将变量设置为1,然后在另一个线程中将变量设置为2,同时让第三个线程读取1,第四个线程读取2同时

在X8664中,您可能是安全的,但是C++的标准没有保证,除非您使用的是处理这个问题的工具。 但比这更糟糕

如果你写信
while(flag == 0) do_thing();