美文网首页
Objective-c 线程系列二 @synchronized

Objective-c 线程系列二 @synchronized

作者: 海森V | 来源:发表于2018-07-03 11:40 被阅读49次

    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和dispatch_once
    正确使用多线程同步锁@synchronized()
    runtime源码
    objc-sync.mm源码

    相关文章

      网友评论

          本文标题:Objective-c 线程系列二 @synchronized

          本文链接:https://www.haomeiwen.com/subject/zukduftx.html