iOS 线程安全之@synchronized同步锁

//  先看问题
- (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 的全部源码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 作者:Ryan Kaplan 译者:徐嘉宏原文地址:More than you want to know abou...
    箪食豆羹阅读 10,683评论 13 100
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 615评论 0 0
  • 老觉得自个憋屈,活的怎么这么憋屈呢。
    君为下阅读 160评论 0 0
  • 发现最近自己很浮躁,心情也不是很美丽,老爸老妈50岁的生日,让我突然发现,我不能再任性了,人活着不能只为自己考虑。...
    成长中的木木阅读 209评论 2 1
  • “现在我们来想象一个人物吧,她叫Maria。”男性A对男性H说,“她是个女的,头发很长,不过总是挽起来——啊!她的...
    realnero阅读 262评论 0 0