美文网首页iOS DeveloperiOS进阶指南
iOS 线程安全之@synchronized同步锁

iOS 线程安全之@synchronized同步锁

作者: 迷路的安然和无恙 | 来源:发表于2017-08-19 22:48 被阅读478次
    //  先看问题
    - (NSString *)xua {
        if (!_xua) {
            @synchronized (_xua) {
                if (!_xua) {
                    _xua = [TAFManager XUA];
                }
            }
        }
        return _xua;
    }
    

    这段代码有什么问题吗?为了保持线程同步,需要给对象加锁。对@synchronized: 防止不同的线程同时执行同一段代码。

    结果

    上面的线程会存在_xuanil的情况(第一次获取值的时候),当被锁定的对象为nil时,其实@synchronized是无作用的,也就是不能保证线程安全。

    原理

    我对 @synchronized的实现十分好奇并搜了一些它的细节。我找到了一些答案,但这些解释都没有达到我想要的深度。锁是如何与你传入 @synchronized 的对象关联上的?@synchronized会保持(retain,增加引用计数)被锁住的对象么?假如你传入 @synchronized 的对象在 @synchronizedblock里面被释放或者被赋值为 nil 将会怎么样?这些全都是我想回答的问题。而我这次的收获,会要你好看。
    @synchronized 的文档告诉我们, @synchronized block 在被保护的代码上暗中添加了一个异常处理。为的是同步某对象时如若抛出异常,锁会被释放掉。@synchronized block 会变成 objc_sync_enterobjc_sync_exit的成对儿调用。我们不知道这些函数是干啥的,但基于这些事实我们可以认为编译器将这样的代码:

    @synchronized(obj) {
        // do work
    }
    转化成这样的东东
    @try {
        objc_sync_enter(obj);
        // do work
    } @finally {
        objc_sync_exit(obj);    
    }
    

    objc_sync_enter 和 objc_sync_exit 是啥?它们是如何实现的?在 Xcode 中按住 Command 键单击它们,然后进到了,里面有我们感兴趣的这两个函数:

    /** 
     * Begin synchronizing on 'obj'.  
     * Allocates recursive pthread_mutex associated with 'obj' if needed.
     * 
     * @param obj The object to begin synchronizing on.
     * 
     * @return OBJC_SYNC_SUCCESS once lock is acquired.  
     */
    OBJC_EXPORT int
    objc_sync_enter(id _Nonnull obj)
        OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0, 2.0);
    
    /** 
     * End synchronizing on 'obj'. 
     * 
     * @param obj The object to end synchronizing on.
     * 
     * @return OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
     */
    OBJC_EXPORT int
    objc_sync_exit(id _Nonnull obj)
        OBJC_AVAILABLE(10.3, 2.0, 9.0, 1.0, 2.0);
    

    不过,objc_sync_enter的文档告诉我们一些新东西: @synchronized 结构在工作时为传入的对象分配了一个递归锁。分配工作何时发生,如何发生呢?它怎样处理 nil?幸运的是 Objective-C runtime 是开源的,所以我们可以马上阅读源码并找到答案!

    注:递归锁在被同一线程重复获取时不会产生死锁。
    想深入了解,有个叫做 NSRecursiveLock 的现成的类也是这样的,你可以试试。
    你可以在这里找到 objc-sync 的全部源码。

    相关文章

      网友评论

        本文标题:iOS 线程安全之@synchronized同步锁

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