使用 Mutex 共享 1 个字的内存
Using Mutex for shared memory of 1 word
我有一个应用程序,其中多个线程访问和写入 1 个字(16 位)的共享内存。
我可以期望处理器在原子操作中读取和写入一个字 from/to 内存吗?所以我不需要共享 memory/variable?
的互斥保护
目标是嵌入式设备运行 VxWorks。
编辑:只有一个 CPU,而且是旧的(>7 年)——我不太确定架构和模型,但我也对 [= 的一般方式更感兴趣18=] CPU 会起作用。如果它是一个 16 位 CPU,那么在大多数情况下,期望它在一次操作中 read/write 一个 16 位变量是否公平?或者我应该在任何情况下都使用互斥保护吗?假设我不关心可移植性,我们谈论 C++98。
问题不是访问的原子性(除非您使用的是 8 位 MC,否则您通常可以假设),而是导致未定义行为的缺少同步。
如果您想编写可移植代码,请改用原子。如果你想为你的特定平台实现最大性能,请非常仔细地阅读你的 OS 和编译器的文档,看看它们为多线程程序提供了哪些额外的机制或保证(但我真的怀疑你会找到更有效的东西比 std::atomic 给你足够的保证)。
所有处理器都将以原子方式读取和写入对齐的机器字,因为如果由另一个处理器读取,您将不会获得旧值的一半位和新值的一半位。
为了获得良好的速度,现代处理器不会将读取-修改-写入操作同步到特定位置,除非您实际要求它 - 因为几乎所有的读取和写入都转到 "non-shared" 位置。
因此,如果该值是我们遇到特定情况或其他一些 "if we read/write an old value, it'll go wrong" 情况的次数的计数器,那么您需要确保两个处理器不会同时更新值。这通常可以通过 atomic
指令(或某种其他形式的 atomic
更新)来完成 - 这将确保一个且只有一个处理器在任何给定时间触及该值,而所有其他处理器当另一个人刚刚进行更新时,处理器不会保留他们认为准确且最新的值的副本。请参阅 C++11 std::atomic
函数集。
注意原子读取或写入机器字值与原子执行整个更新之间的区别。
Can I expect that the processor reads and writes a word from/to memory in an atomic operation?
是的。
So I don't need mutex protection of the shared memory/variable?
没有。考虑:
++i;
即使读取和写入是原子的,同时执行此操作的两个线程也可以分别读取、每次递增,然后每次写入,从而导致在需要两次的情况下只有一次递增。
Can I expect that the processor reads and writes a word from/to memory in an atomic operation?
是的,如果数据正确对齐并且不大于机器字,大多数 CPU 指令将按照您描述的方式对其进行原子操作。
So I don't need mutex protection of the shared memory/variable?
您确实需要一些同步 - 无论是互斥锁还是使用原子操作 std::atomic
。
原因包括:
如果您的变量不是 volatile
,编译器甚至可能不会为名义上在您可能期望的位置保存该变量的内存地址发出读写指令,而是重用读取的值或更早设置保存在 CPU 寄存器中或在编译时已知
- 如果您使用互斥锁或
std::atomic
类型,您也不需要使用 volatile
此外,即使数据写入内存,它也可能不会离开 CPU 缓存并写入其他内核和 CPU 可以看到的实际 RAM它除非你明确地使用内存屏障(std::mutex
和 std::atomic
类型为你做)
最后,读取和写入值之间的延迟可能会导致意外结果,因此像 ++x
这样的操作可能会失败,正如 David Schwartz 所解释的那样。
我有一个应用程序,其中多个线程访问和写入 1 个字(16 位)的共享内存。
我可以期望处理器在原子操作中读取和写入一个字 from/to 内存吗?所以我不需要共享 memory/variable?
的互斥保护目标是嵌入式设备运行 VxWorks。
编辑:只有一个 CPU,而且是旧的(>7 年)——我不太确定架构和模型,但我也对 [= 的一般方式更感兴趣18=] CPU 会起作用。如果它是一个 16 位 CPU,那么在大多数情况下,期望它在一次操作中 read/write 一个 16 位变量是否公平?或者我应该在任何情况下都使用互斥保护吗?假设我不关心可移植性,我们谈论 C++98。
问题不是访问的原子性(除非您使用的是 8 位 MC,否则您通常可以假设),而是导致未定义行为的缺少同步。 如果您想编写可移植代码,请改用原子。如果你想为你的特定平台实现最大性能,请非常仔细地阅读你的 OS 和编译器的文档,看看它们为多线程程序提供了哪些额外的机制或保证(但我真的怀疑你会找到更有效的东西比 std::atomic 给你足够的保证)。
所有处理器都将以原子方式读取和写入对齐的机器字,因为如果由另一个处理器读取,您将不会获得旧值的一半位和新值的一半位。
为了获得良好的速度,现代处理器不会将读取-修改-写入操作同步到特定位置,除非您实际要求它 - 因为几乎所有的读取和写入都转到 "non-shared" 位置。
因此,如果该值是我们遇到特定情况或其他一些 "if we read/write an old value, it'll go wrong" 情况的次数的计数器,那么您需要确保两个处理器不会同时更新值。这通常可以通过 atomic
指令(或某种其他形式的 atomic
更新)来完成 - 这将确保一个且只有一个处理器在任何给定时间触及该值,而所有其他处理器当另一个人刚刚进行更新时,处理器不会保留他们认为准确且最新的值的副本。请参阅 C++11 std::atomic
函数集。
注意原子读取或写入机器字值与原子执行整个更新之间的区别。
Can I expect that the processor reads and writes a word from/to memory in an atomic operation?
是的。
So I don't need mutex protection of the shared memory/variable?
没有。考虑:
++i;
即使读取和写入是原子的,同时执行此操作的两个线程也可以分别读取、每次递增,然后每次写入,从而导致在需要两次的情况下只有一次递增。
Can I expect that the processor reads and writes a word from/to memory in an atomic operation?
是的,如果数据正确对齐并且不大于机器字,大多数 CPU 指令将按照您描述的方式对其进行原子操作。
So I don't need mutex protection of the shared memory/variable?
您确实需要一些同步 - 无论是互斥锁还是使用原子操作 std::atomic
。
原因包括:
如果您的变量不是
volatile
,编译器甚至可能不会为名义上在您可能期望的位置保存该变量的内存地址发出读写指令,而是重用读取的值或更早设置保存在 CPU 寄存器中或在编译时已知- 如果您使用互斥锁或
std::atomic
类型,您也不需要使用volatile
- 如果您使用互斥锁或
此外,即使数据写入内存,它也可能不会离开 CPU 缓存并写入其他内核和 CPU 可以看到的实际 RAM它除非你明确地使用内存屏障(
std::mutex
和std::atomic
类型为你做)最后,读取和写入值之间的延迟可能会导致意外结果,因此像
++x
这样的操作可能会失败,正如 David Schwartz 所解释的那样。