一个整数是否可以安全读取,而另一个线程可能正在写入?

时间:2016-06-22 09:14:41

标签: c++ multithreading

我遇到了很多不同的线程写入和读取一些数据的情况,并且互斥开销实际上是在扼杀性能,所以我尽量尽量减少它们的使用和持续时间。

参考:Why is reading not thread-safe?

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

一个整数(编译为x64时为64位)读取是否安全呢?

4 个答案:

答案 0 :(得分:3)

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

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

使用atomic<T>类型。它适用于性能,甚至可以在支持它的平台上无锁。

答案 1 :(得分:2)

它不只是关于阅读&#34;腐败&#34;更新,它是关于它们的排序和可见性。您可以在一个线程中设置一个值,而不会在另一个线程中看到它。您可以按顺序设置两个变量,并在另一个线程中以相反的顺序更改它们。您可以在一个线程中将变量设置为1,然后在另一个线程中将变量设置为2,并将第三个线程设置为1,第四个线程同时显示为2.

在x86_64上,你可能对大多数这些都是安全的,但除非你使用设备来处理这个问题,否则C ++标准都不能保证这一点。

但是,它比那更糟。

如果你写:

while(flag == 0) do_thing();

在一个线程中,认为你可以在另一个线程中写1来退出循环,它可能不起作用。允许编译器假设数据的值不会在其下发生变化,并且不会在每个循环中实际测试它。 再次,使用提供的设施来保证它的工作。

您应该使用std::atomic<int>,这将是安全且高效的,您可以在平台上安全地进行操作。如果你真的知道你在做什么,你可以使用原子与释放并获得更高效的语义 - 但是,基于你提出这个问题的事实,你可能不应该做更多的研究,并且只需使用原子的默认模式。

答案 2 :(得分:0)

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

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

只需使用std::atomic<int>并(安全地)完成它。

答案 3 :(得分:0)

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

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

但实际上,如果您只有一个更新线程并且您不关心任何特定时间范围内其他线程是否可以看到更新,那么可能可以(例如,您正在更新)线程设置一个标志,表示已按下Control-C,应用程序可以在下一个方便的时刻关闭,但是如果它在1ms或2秒之后无关紧要,那么你可能没问题。

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