Objective-c 线程系列二 @synchronized

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源码

你可能感兴趣的:(Objective-c 线程系列二 @synchronized)