如果您有一个多线程应用程序,在线程之间共享一个字变量(即 - 32 位系统上的 32 位数据类型),是否有必要使用互斥锁保护对该字的读取和写入?
2 回答
一般来说,是的,您应该使用互斥锁同步访问。即使您认为对内存位置的写入将是“原子的”,CPU 也可能重新排序读取和写入该内存的指令,从而导致不需要的行为。此外,在多核或多 CPU 系统上,如果没有同步,其他内核/CPU 可能不会立即看到对共享内存位置的写入。
有关更多信息,请参阅有关Memory Barrier的 Wikipedia 文章。
而不是谈论缓存、多核、乱序执行等。我将提供一个简单的示例来说明为什么要使用互斥锁(或其他一些互斥技术,例如中断操作等)。 )
为了将变量增加一——至少在我现在处理的大多数架构中——你必须将值从内存读取到寄存器,调整寄存器中的值,然后将其写回内存。假设我们有 2 个线程,高优先级 A 和低优先级 B。以这种情况为例:
- 线程 B 从内存中将“x”读入寄存器(值为 5)。
- 线程 B 将寄存器的值增加到 6。
- 线程 A 抢占线程 B。
- 线程 A 将“x”读入寄存器(内存中的值仍然是 5,对吧?)。
- 线程 A 将寄存器的值增加到 6。
- 线程 A 将值 6 写回内存
- 线程 A 进入睡眠状态。
- 线程 B 唤醒,并将其寄存器值 6 写回内存。
现在两个线程都增加了值,但它只增加了 1。
如果这是电梯楼层数、感应到的心跳次数或航天飞机倒计时值(好吧,那将是一个递减值),我们可能会遇到问题。
请注意,有一些架构(通常是 CISC)可以原子地执行此增量操作,最好不要做出此假设(您好,可移植性和正确性)。
注意:在某些情况下——但不要这样做——如果你有一个作家和多个读者,你可以在没有互斥锁的情况下逃脱。例如,也许 ISR 会增加滴答计数或其他任何东西,而其他线程/任务会时常出现并读取它。
我想我想说的是,保护共享数据总是一个好主意,即使你认为没有它你可以逃脱。如果你想“去突击队”,你真的必须知道你在做什么,即使那样,同样的代码明天移植到新架构时可能会中断。