【发布时间】:2011-06-09 10:02:18
【问题描述】:
Objective-C 属性默认为atomic,这确保访问器是原子的,但不能确保整体线程安全(根据this question)。我的问题是,在大多数并发场景中,原子属性不是多余的吗?例如:
场景 1:可变属性
@interface ScaryMutableObject : NSObject {}
@property (atomic, readwrite) NSMutableArray *stuff;
@end
void doStuffWith(ScaryMutableObject *obj) {
[_someLock lock];
[obj.stuff addObject:something]; //the atomic getter is completely redundant and could hurt performance
[_someLock unlock];
}
//or, alternatively
void doStuffWith(ScaryMutableObject *obj) {
NSMutableArray *cachedStuff = obj.stuff; //the atomic getter isn't redundant
[_someLock lock];
[cachedStuff addObject:something]; //but is this any more performant than using a nonatomic accessor within the lock?
[_someLock unlock];
}
场景 2:不可变属性
我在想,在使用不可变对象时,原子属性可能有助于避免锁定,但由于不可变对象可以指向 Objective-C 中的可变对象,所以这并没有太大帮助:
@interface SlightlySaferObject : NSObject {}
@property (atomic, readwrite) NSArray *stuff;
@end
void doStuffWith(SlightlySaferObject *obj) {
[[obj.stuff objectAtIndex:0] mutateLikeCrazy];//not at all thread-safe without a lock
}
我能想到在没有锁的情况下使用原子访问器是安全的(因此完全值得使用原子属性)的唯一场景是:
- 使用以下属性 原语;
- 使用以下属性
保证是不可变的而不是
指向可变对象(例如
NSString或NSArray的 不可变对象)。
我错过了什么吗?还有其他使用原子属性的好理由吗?
【问题讨论】:
-
我认为帖子链接的另一个 SO 问题很好地涵盖了它。 atomic 仅涵盖对属性的 access ——例如您无法取回半旧半新的 int,它会是全新的或全新的(在 obj-c 的情况下,它还可以保证保留计数)。通常,可能需要全锁/屏障。但是,原子访问(不是 CAS,这是不同的)可以在一些“无锁”算法和/或 just-get-me-out 线程循环中使用。所以是的,它们覆盖的范围有限。
-
@pst 完全正确,一些 ABI 上的一些标量类型可能是部分的。我认为 PPC 会将特定标量类型的一半放在寄存器中,另一半放在堆栈中,这样您实际上可以在其他单值标量上获得一半新一半旧的行为!
标签: iphone objective-c multithreading cocoa properties