当两个线程“同时”将 BOOL 设置为 YES 时会发生什么?
那么它的值就是YES。如果线程将相同的值写入相同的内存位置,则内存位置将具有该值,无论它是否是原子的都不起作用。只有当两个线程将不同的值写入同一个内存位置,或者一个线程写入它而另一个线程正在读取它时,它才会起作用。
Objective C 中的 BOOL 读/写是原子的吗?
如果您的硬件是运行 macOS 的 Macintosh。 BOOL 在 PPC 系统上是 uint32_t,在 Intel 系统上是 char,编写这些数据类型在它们各自的系统上是原子的。
但是,Obj-C 语言没有这样的保证。在其他系统上,这取决于您使用的编译器以及如何为该平台定义BOOL。大多数编译器(gcc、clang、...)保证写入int-size 的变量始终是原子的,其他大小是否原子取决于CPU。
请注意,原子与线程安全不同。写BOOL 不是内存屏障。编译器和 CPU 可能会围绕 BOOL 对指令重新排序:
a = 10;
b = YES;
c = 20;
无法保证指令按该顺序执行。 b 是 YES 的事实并不意味着 a 是 10。编译器和 CPU 可以根据需要随意调整这三个指令,因为它们不相互依赖。显式原子指令以及锁、互斥体和信号量通常是内存屏障,这意味着它们指示编译器和 CPU 不要将位于该操作之前的指令移到它之外,也不要将位于该操作之后的指令移到它之前(这是一个硬边界,该指令可能不会通过)。
也不保证缓存一致性。即使您将BOOL 设置为YES,其他一些线程仍可能在有限的时间内将其视为NO。内存屏障操作通常也是确保系统中所有线程/内核/CPU之间缓存同步的操作。
并且在这里添加一些真正有用的东西,这是您如何确保设置布尔值是原子的,并在 2020 年使用 C11 充当内存屏障,这也将在 Obj-C 代码中工作:
#import <stdatomic.h>
// ...
volatile atomic_bool b = true;
// ...
atomic_store(&b, true);
// ...
atomic_store(&b, false);
此代码不仅可以保证对 bool 进行原子写入(系统将为其选择适当的类型),它还将充当内存屏障(顺序一致)。
要从另一个线程以原子方式读取布尔值,您可以使用
bool x = atomic_load(&b);
您还可以使用atomic_load_explicit 和atomic_store_explicit 并传递显式内存顺序,这使您可以更细粒度地控制允许哪些内存重新排序,哪些不允许。
在此处详细了解您的可能性:
http://llvm.org/docs/Atomics.html
请务必阅读“优化器注意事项”以查看允许哪些内存重新排序。如果有疑问,请始终使用顺序一致(memory_order_seq_cst,如果未指定,则为默认值)。它不会带来最快的性能,但它是最安全的选择,而且你真的应该只在你知道自己在做什么的情况下使用其他东西。