美文网首页
系统底层源码分析(5)——@synchronized

系统底层源码分析(5)——@synchronized

作者: 无悔zero | 来源:发表于2021-01-08 15:39 被阅读0次

开发中经常遇见的@synchronized,平常都说在多线程中它不是安全的,今天就从底层探索一下它为什么不安全。先看个例子:

_testArr = [NSMutableArray array];
for (int i= 0; i< 1000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self creatNewArray];
    });
}
- (void)creatNewArray {
    NSMutableArray *newArr =[NSMutableArray array];
    @synchronized (_testArr) {//无法保证多线程安全;
        _testArr = newArr;//崩溃
    }
}
打开僵尸指针运行

崩溃原因可以理解为,比如有线程1线程2线程3线程1线程2同时锁的是_testArr,在线程1括号里,_testArr变成了newArr,那么轮到线程2时,_testArr不是最初的数组了,而线程3上锁的时候,锁的是newArr,所以不是同一把锁,这样在多线程下就像没有锁一样。

  • 接下来在运行时断点进入汇编查看,这便是@synchronized的调用:
  1. 那么我们就从objc4-750源码探究一下这两个方法:
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;
}

在内部其实加了递归锁,对象会被SyncData保存作为锁的关联:

  1. 结束时就是解锁:
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;
}
  • 解决

所以使用时一般锁不变的对象,比如可以锁self

- (void)creatNewArray {
    NSMutableArray *newArr =[NSMutableArray array];
    @synchronized (self) {
        _testArr = newArr;
    }
}

相关文章

网友评论

      本文标题:系统底层源码分析(5)——@synchronized

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