Objective-c 线程系列一 atomic是安全的吗
Objective-c 线程系列二 @synchronized
Objective-c 线程系列三 NSRecursiveLock
一 @synchronized 作用
它防止不同的线程同时执行同一段代码。
二 测试线程安全的代码,不安全把 @synchronized注释掉
-(void)synchronized{
NSMutableArray *a = [[NSMutableArray alloc] init];
_number = 15;
//获得全局队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
//循环,把50个任务放到队列中,异步执行;队列是并发的,50个人任务是并发的
for (int i = 0; i < 50; i++) {
dispatch_async(globalQueue, ^{
@synchronized(a){
if (_number > 0) {
[NSThread sleepForTimeInterval:1];
_number --;
NSLog(@"_number %ld",_number);
}else{
NSLog(@"红包个数小于0");
}
}
});
}
}
三 @synchronized()参数传入问题
1 如果传入 nil, @synchronized(nil){} ,{}中的代码不能保证是原子性,不能保证同一时间同一个线程执行
2 慎用@synchronized(self)
这样使用,容易导致死锁的出现。原因为self很可能会被外部对象访问,被用作key来生成一锁。两个公共锁交替使用的场景就容易出现死锁。比如
//class A
@synchronized (self) {
[_sharedLock lock];
NSLog(@"code in class A");
[_sharedLock unlock];
}
//class B
[_sharedLock lock];
@synchronized (objectA) {
NSLog(@"code in class B");
}
[_sharedLock unlock];
3 参数的传入应该是一个是类内部维护的NSObject对象,而且这个对象是不可见,比如一个数组的添加和移除
@synchronized(self.taskObservers){
}
四 @synchronized(self) 如何查看源码
1 在一个函数写以下代码
@synchronized(self){ }
2 点击 Product->Perform Action ->Assemble
3 会看到关键的代码
_objc_sync_enter
_objc_sync_exit
4 打开链接 ,objc-sync.mm源码,搜索objc_sync_enter,我们来看下objc_sync_enter方法的实现
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE);
if (!data) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();
if (!okay) {
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {
// @synchronized(nil) does nothing
}
return result;
}
五 @synchronized() 源码解读
synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。所以管是传入什么类型的object,只要是有内存地址,就能启动同步代码块的效果
六 记录一个问题
@synchronized (self.array) {
if ([self.array containsObject:eventKey]) {
return YES;
}
}
UncaughtException=>NSGenericException', reason: '*** Collection <__NSArrayM: > was mutated while being enumerated.
修改
临时变量的方式
未完待续....
参考文档
苹果开发者文档
细说@synchronized和dispatch_once
正确使用多线程同步锁@synchronized()
runtime源码
objc-sync.mm源码