atomic
在objc
中是用来修饰属性,表示属性的写操作是一个原子操作。本文以几段代码为例,分析为什么atomic
修饰的属性并不是线程安全的。
定义一个带有atomic
属性的Model
@interface Model : NSObject
@property (atomic, assign) NSInteger index;
@end
@implementation Model
@end
首先我们看下述代码代码一。输出结果大家都知道是无序的。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
Model *m = [Model new];
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
m.index++;
NSLog(@"index = %ld", m.index);
});
}
这段代码代码二,大家也知道结果,输出是有序的。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSLock *lock = [NSLock new];
Model *m = [Model new];
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
[lock lock];
m.index++;
NSLog(@"index = %ld", m.index);
[lock unlock];
});
}
接下来,我们拆分一下有序的代码,代码三。ok,这部分也是有序的,没有问题。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSLock *lock = [NSLock new];
Model *m = [Model new];
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
[lock lock];
m.index++;
[lock unlock];
[lock lock];
NSLog(@"index = %ld", m.index);
[lock unlock];
});
}
调整一下代码,代码四:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSLock *lock = [NSLock new];
Model *m = [Model new];
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
m.index++;
[lock lock];
NSLog(@"index = %ld", m.index);
[lock unlock];
});
}
这段代码可能就需要思考一下了。首先m.index++;
是原子的,NSLog(@"index = %ld", m.index);
也是原子的,那么应该能顺序输出。但实际运行代码后发现,还是无序的。
最后一个例子,代码五:
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
NSLock *lock1 = [NSLock new];
NSLock *lock2 = [NSLock new];
Model *m = [Model new];
for (int i = 0; i < 100; i++) {
dispatch_async(queue, ^{
[lock1 lock];
m.index++;
[lock1 unlock];
[lock2 lock];
NSLog(@"index = %ld", m.index);
[lock2 unlock];
});
}
可能已经被绕晕了,如果单独拿出这段代码,大家很快就能回答出来,无序的。
看完这五个例子,大家应该已经有答案了。实际上atomic
能保证属性的写操作是原子的,导致atomic
不能保证线程安全的原因是问题的规模。问题规模的扩大,导致锁操作的范围需要扩展,而属性的原子操作对于扩大规模后问题的解决没有任何帮助,所以atomic
并不能保证线程安全。