合成基元中的原子实际上意味着什么?

6
在Android中,我可以安全地访问和修改来自不同线程的原始类型。我使用这个方法在OpenGL绘制循环和主线程Android UI中修改的用户设置之间共享数据。通过将每个设置存储在一个原始类型中,并使每个独立于其他变量的值,可以在不使用锁或同步关键字的情况下安全地修改所有这些变量。
在Objective-C中也是如此吗?我读到,在变量上放置atomic本质上会导致合成的getter和setter使用锁,类似于在Java中使用同步方法。我还读到,这样做的原因是为了防止对象在被另一个线程读取时部分修改。
但是像Java一样,原始类型是否免受部分修改的影响呢?如果是这样的话,似乎我可以使用Java中相同的范例在线程之间共享数据。但是,对于原始类型,atomic关键字似乎是无意义的,是吗?
我还读到,比使用atomic变量更强大而且更快的解决方案是在从多个线程访问的对象使用之前复制它们。但我不确定如何实现这一点。一个非原子对象在被复制时不能被修改,从而损坏副本,是吗?

1
为什么不使用原子类型来声明基本类型以确保线程安全? - John
可能是原子属性 vs 非原子属性的重复问题。 - bbum
@user1256663 我听说它们非常慢,而我正在编写一个OpenGL应用程序。帧速率是个问题。 - Tenfour04
3个回答

6

原始类型不能保证不受部分修改的影响,因为在C语言中,对原始类型的修改并不能保证是原子操作,而Objective-C继承自C语言。C语言仅保证了序列点,并没有要求两个序列点之间的处理是原子的——一个经验法则是每个完整表达式都是一个序列点。

实际上,修改原始类型通常是一个两步过程;修改在寄存器中进行,然后写入内存。很少有情况下写入本身不是原子的,但也不能保证它何时发生与修改。即使使用了volatile限定符,提供的唯一保证是序列点。

苹果公司通过OSAtomic.h暴露了一些C函数,用于原子操作,这些函数直接映射到CPU提供的专门的原子指令,用于实现并发机制。可能可以直接使用其中之一,而无需使用笨重的互斥锁。

Objective-C中常见的模式有:

  • 不可变对象和函数式转换 —— 这也是内存管理的原因之一,但这部分是为什么NSStringNSArray等与NSMutableStringNSMutableArray等特别不同的原因;
  • 串行调度队列,可以通过在队列上复制、修改、替换来组合使用;
  • 适当使用@synchronizedNSConditionLock或其他显式同步机制。

主线程本身就是一个串行调度队列,因此如果限制自己只使用它,你可以完全忽略并发问题。


3

原子合成的@属性在并发部分更新时不受影响。在这种体系结构下,访问器方法将使用锁。

通常情况下,在C语言中的基本类型不一定安全,可能存在并发部分更新的风险。


-2

我不相信你可以部分修改原始类型,这是它们成为原始类型的一部分。你要么修改它,要么不修改。从这个意义上说,我会说它们是线程安全的。

当你说原子关键字对于原始类型毫无意义时,你是正确的。

有人已经在这里尝试过了: Objective-c properties for primitive types


1
只要原始类型已经对齐,在大多数体系结构上,您无法“看到”部分修改的原始类型。 - Hot Licks
1
取决于原语和架构。例如,一些ARM架构使用8字节双精度浮点数,但正式上只承诺4字节加载和存储的原子性。 - Greg Parker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接