var ++或var-- not threadsafe?

时间:2013-03-23 22:21:32

标签: c++ thread-safety increment nonblocking

在C ++中,我觉得我总是认为像var ++和var--这样的东西是合理的线程安全的 - AKA - 你可以保证你的价值会在某个时间点增加或减少。

正是基于这种信念,我建立了对线程安全操作的非阻塞算法的理解。 然而,今天我感到震惊,因为我有一个没有增加的变量 - 因此我质疑我过去工作的大量有效性。

在一个小程序中,我有一个初始化为0的全局变量。 启动了8个P-Threads,每个调用varname ++总共1024次,总计增加到8 * 1024。 但是在所有线程完成执行后,varname的值明显小于8 * 1024。

我在这里错过了这艘船吗? 有人可以赐教我吗?

3 个答案:

答案 0 :(得分:5)

究竟是什么让你相信哪些线程安全?一般来说,他们不是。原因很简单:算术通常在寄存器上完成,因此var++可能会转换为如下所示:

load var to R1
inc R1
store R1 to var

如果另一个线程在加载和商店之间修改var,您显然会松开该更新。实际上这个问题会更糟,因为编译器可以决定将变量保存在寄存器中,只要它能够(只要它可以证明var不能通过任何指针访问它(在同一个线程中))。

让多个线程访问同一个变量被(C ++ 11)标准定义为数据争用(因此是未定义的行为),除非线程没有修改变量(如果所有线程都只读取访问权限,那么你没事)。

对于线程安全操作,您需要使用锁定(例如在C ++ 11中使用std::mutex)或原子操作。如果您使用C ++ 11,则可以使用std::atomic<int>(或您的计数器所使用的任何类型)作为var的类型来获取线程安全修改。 (算术)std::atomic<T>上的操作(如增量和减量运算符)保证是标准的线程安全。

答案 1 :(得分:4)

C ++的增量前和增量后运算符线程安全。

类似的问题:I've heard i++ isn't thread safe, is ++i thread-safe?

答案 2 :(得分:2)

是的,你错过了什么 - 即你的读写不是原子的。因此,许多线程可以读取该值,然后递增它,然后将其写回,如果这些操作全部“并行”发生,则该值将仅增加1。

您可以使用C ++ 11(或Boost)的std :: atomic类型包装器修复此问题,如下所述: http://en.cppreference.com/w/cpp/atomic/atomic