自旋锁安全问题验证!
在oc中有关键字nonatomic和atomic,atomic修饰的属性是写安全的,但绝不是线程安全。
实际上atomic修饰的属性只是多了一层自旋锁,其作用是对属性的setter方法加锁,当一个新的线程p1要尝试setter方法写操作时,需要获取的这个自旋锁,若此时已有线程p2正在写(已经占有自旋锁),那么p1获取锁操作将不断死循环自旋在那里,直到该自旋锁的保持者释放了锁。
由上可知道两点:
- 自旋锁是一个性能损耗严重的锁,使用自旋锁的属性的setter方法里绝对不要进行太多的操作
- 自旋锁不是绝对线程安全的
atomic的线程不安全性验证
通过如下方法可以验证:
@property (atomic, assign) int atomicA;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_atomicA=0;
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); // 并发队列,异步执行
[self testForAtomicAInqueue:queue];
dispatch_barrier_async(queue, ^(){NSLog(@"-----result, _atomicA=%d",self.atomicA,);});
}
- (void) testForAtomicAInqueue:q{
dispatch_async(q, ^{
for (int i = 0;i<15;++i){
self.atomicA=self.atomicA+1;
NSLog(@"_atomicA+1= %d",self.atomicA);
}
});
dispatch_async(q, ^{
for (int i = 0;i<15;++i){
self.atomicA = self.atomicA-1;
NSLog(@"_atomicA-1= %d",self.atomicA);
}
});
}
最终打印结果atomicA可能会有0、-1、1 三中情况,原因是atomic标签的属性只是setter安全,而get则没有锁的限制,考虑这么一个情况,atomicA初始值=0,两个线程p1、p2分别对atomicA进行+1、-1,由于双方同时get到atomicA=0;接着同时(注意不是同时刻)setter,那么最终的结果就只能是1或-1了,这取决于谁的执行时刻要后面一点,例如按照如下流程,则结果为-1
来自2021年的补充
最近又跑了一下发现最后结果是唯一的 0 了,可能iOS系统做了优化,目前存疑!
以上
既已览卷至此,何不品评一二: