9

当两个线程“同时”将 BOOL 设置为 YES 时会发生什么?

4

4 回答 4

8

这是Jacko建议的解决方案代码。与和一起
使用volatile uint32_tOSAtomicOr32BarrierOSAtomicAnd32Barrier

#import <libkern/OSAtomic.h>

volatile uint32_t _IsRunning;

- (BOOL)isRunning {
    return _IsRunning != 0;
}

- (void)setIsRunning:(BOOL)allowed {

    if (allowed) {
        OSAtomicOr32Barrier(1, & _IsRunning); //Atomic bitwise OR of two 32-bit values with barrier
    } else {
        OSAtomicAnd32Barrier(0, & _IsRunning); //Atomic bitwise AND of two 32-bit values with barrier.
    }
}
于 2014-01-28T11:19:17.517 回答
7

不。没有锁定结构,在 Objective C 中读/写任何类型变量都不是原子的。

如果两个线程同时向 BOOL 写入 YES,则无论哪个线程先进入,结果都是 YES。

请参阅:同步线程执行

于 2010-02-14T03:00:21.997 回答
5

我将不得不偏离公认的答案。对不起。虽然目标 c 不能保证声明为非原子的 BOOL 属性实际上是原子的,但我不得不猜测您最关心的硬件(所有 iOS 和 macOS 设备)具有以原子方式执行字节读取和存储的指令。因此,除非 Apple 推出在 IBM 微控制器上运行的 Road Light OS,该微控制器具有 5 位宽总线来发送 10 位字节,否则在需要原子 BOOL 的情况下也可以使用非原子 BOOL。该代码不能移植到 Road Light OS,但如果您可以牺牲代码的未来证明,非原子对于此用例来说是可以的。我敢肯定有一些顽固的人,所以这将提出分解原子/非原子情况下合成的 BOOL getter 和 setter 的挑战,看看是什么' s 的区别。至少在ARM上。

你从中得到的可能是这个

  1. 您可以将 BOOL 属性声明为原子属性,并且在所有硬件 iOS 和 macOS 内在支持上都不会花费您一毛钱。
  2. 内存屏障与原子性正交
  3. 你绝对不应该使用 4 字节属性来存储布尔值,除非你进入 [非常] 模糊逻辑。这是愚蠢和浪费,你不想成为一个 Java 程序员的克隆,谁不能区分浮点数和双精度数,或者是吗?
  4. BOOL 变量(显然不支持原子/非原子装饰器在某些狭窄的总线架构上不会是原子的,目标 C 无论如何都不会被使用(有或没有一些 [非常] 微型操作系统的微控制器是 C 和汇编领域我想。他们不'通常不需要 objc 运行时会带来的行李)
于 2016-08-09T10:37:05.500 回答
4

当两个线程“同时”将 BOOL 设置为 YES 时会发生什么?

那么它的值将是YES。如果线程将相同的值写入相同的内存位置,则内存位置将具有该值,无论它是否是原子的都不起作用。只有当两个线程将不同的值写入同一个内存位置,或者一个线程写入它而另一个线程正在读取它时,它才会起作用。

BOOL 在 Objective C 中是读/写原子的吗?

如果您的硬件是运行 macOS 的 Macintosh。BOOLuint32_tPPC 系统和charIntel 系统上,编写这些数据类型在它们各自的系统上是原子的。

但是,Obj-C 语言没有这样的保证。在其他系统上,这取决于您使用的编译器以及BOOL该平台的定义方式。大多数编译器(gcc,clang,...)保证写入int-size 的变量始终是原子的,其他大小是否是原子的取决于 CPU。

请注意,原子与线程安全不同。写 aBOOL不是记忆障碍。编译器和 CPU 可能会围绕BOOL写入重新排序指令:

a = 10;
b = YES;
c = 20;

不能保证指令按该顺序执行。事实上,这b并不YES意味着a10。编译器和 CPU 可以根据需要自由地对这三个指令进行混洗,因为它们不相互依赖。显式原子指令以及锁、互斥体和信号量通常是内存屏障,这意味着它们指示编译器和 CPU 不要将位于该操作之前的指令移到它之外,也不要将位于该操作之后的指令移到它之前(这是一个硬边界,该指令可能不会通过)。

也不保证缓存一致性。即使您将 a 设置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);

You can also use atomic_load_explicit and atomic_store_explicit and pass an explicit memory order, which allows you to control more fine grained which kind of memory reordering is allowed and which ones is not.

Learn more about your possibilities here:

http://llvm.org/docs/Atomics.html

Always read "Notes for optimizers" to see which memory reordering is allowed. If in doubt, always use Sequentially Consistent (memory_order_seq_cst, which is the default if not specified). It will not result in fastest performance but it's the safest option and you really should only use something else if you know what you are doing.

于 2020-08-31T18:35:19.790 回答