目标C原子属性线程安全
我读过几个职位对原子和写入演示验证线程安全的,这样目标C原子属性线程安全
@property(atomic,assign) NSInteger sum;
//然后做这个
for (NSInteger i = 0; i<1000; i++) {
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
self.sum++;
});
}
使财产“总和”作为原子性质;并启动1000个并发线程来添加一个;
虽然结果是1000,但它不是,如果我添加一个NSLock来包装self.sum ++,结果是1000;
有人帮我解释一下吗?
这有几个层次。
首先,声明的属性大多只是声明存取方法的捷径。如果不提供自己的实现,则默认情况下发生的编译器综合属性会定义这些方法和实例变量以支持该属性。
所以,这样的:
@property(atomic,assign) NSInteger sum;
基本上只是这样的:
- (NSInteger) sum;
- (void) setSum:(NSInteger)value;
属性的合成产生的那些方法的一个实例变量和实施方式:
@implementation ...
{
NSUInteger _sum;
}
- (NSInteger) sum
{
// ...
}
- (void) setSum:(NSInteger)value
{
// ...
}
对于原子性质,实现-sum
和-setSum:
各自保证运行,以至于两者都不会中断另一方。与-setSum:
调用“同时”发生的-sum
的调用将返回-setSum:
之前的值或之后的值,但不会返回部分修改的frankenstein值或任何临时值。同样,同时拨打-setSum:
的两个电话将导致_sum
具有来自其中一个或另一个电话的值,但从不会有一些混合或临时值。这两个电话似乎是以严格的顺序发生的,无论是A然后B还是B然后A是任意的。
这对于具有复合类型的房产比较容易理解,如NSRect
。设置该属性的两个线程永远不会导致例如来自一个线程的origin
和来自另一个线程的size
被存储。一方或另一方将“赢”,并且协议将保持一致。同样,调用getter的线程也不会看到混合值,即使它与调用者的调用同时发生。
接下来,使用点语法访问属性(例如self.sum
)实际上只是调用访问器的快捷方式。因为只有get和set访问,并没有任何“增值”访问,就像self.sum++;
需求的语句来完成这两个,分别:
[self setSum:[self sum] + 1];
所以,你的语句首先涉及到一个呼叫-sum
然后到-setSum:
通话每个线程。没有什么能够确保其他线程不能彼此交错操作。该属性的原子性并不妨碍它。也就是说,线程A可以从它的调用-sum
得到值5,线程B也可以从它的调用-sum
得到值5,每一个都可以计算6作为新值,然后他们都称-setSum:
与值6。所以,两个线程都“增加”的属性,但它只会增加了1
总之,原子是不是线程安全的。认为它是一个概念错误。这只是原子性。它确实可以防止多线程同时访问同一个属性时可能发生的一种腐败,但不是所有类型的。
很详细的说明,THX – NickYu
你能在原子属性添加补充说明,当开发人员定义自己的getter和setter。换句话说,如果我声明一个原子属性并提供访问器实现,那么我还有一个原子属性吗? –
@TimReddy,如果财产申报原子,那么你有责任确保原子性,当你实现存取。一般情况下,你不能与编译器生成的访问使用,以保持原子性,因此,如果您实现一个原子读写属性的访问的一个机制进行互操作,必须同时实现。如果你的实现无法维护声明中承诺的原子性,那么它们就是bug。 –
尝试这样的:
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i<1000; i++) {
dispatch_async(queue, ^{
NSLock *aLock = [[NSLock alloc] init];
[aLock lock];
self.sum++;
[aLock unlock];
});
}
尝试仍然不是1000 – NickYu
你得到什么结果呢? – rmaddy
为什么你在循环内而不是在循环之前创建队列? – rmaddy
997 998 996不1000,在循环,使1000个线程循环之前 – NickYu