iOS底层探索之多线程(十七)——通过 Swift的Foundation源码分析锁(NSLock、NSCondition、NSRecursiveLock)

iOS 开发,各种锁你了解多少?NSLock、NSCondtion、NSRecursiveLock.......

回顾

上篇博客中已经对NSLockNSCondtionNSRecursiveLockNSCondition等锁进行了举例分析,那么本篇博客就继续分析锁,从Foundation源码分析锁!

关于锁你了解多少?

iOS底层探索之多线程(一)—进程和线程

iOS底层探索之多线程(二)—线程和锁

iOS底层探索之多线程(三)—初识GCD

iOS底层探索之多线程(四)—GCD的队列

iOS底层探索之多线程(五)—GCD不同队列源码分析

iOS底层探索之多线程(六)—GCD源码分析(sync 同步函数、async 异步函数)

iOS底层探索之多线程(七)—GCD源码分析(死锁的原因)

iOS底层探索之多线程(八)—GCD源码分析(函数的同步性、异步性、单例)

iOS底层探索之多线程(九)—GCD源码分析(栅栏函数)

iOS底层探索之多线程(十)—GCD源码分析( 信号量)

iOS底层探索之多线程(十一)—GCD源码分析(调度组)

iOS底层探索之多线程(十二)—GCD源码分析(事件源)

iOS底层探索之多线程(十三)—锁的种类你知多少?

iOS底层探索之多线程(十四)—关于@synchronized锁你了解多少?

iOS底层探索之多线程(十五)—@synchronized源码分析

iOS底层探索之多线程(十六)——锁分析(NSLock、NSCondtion、NSRecursiveLock、NSCondition)

1. 从Foundation源码分析锁

上篇博客对NSLockNSCondtionNSRecursiveLock等锁的使用,进行了代码的举例和分析,那么他们其实都是对pthread锁进行了一层封装。通过查看 NSLockAPI,发现是在Foundation框架下的如图:

NSLock的 API

通过查看 API 发现,锁都是遵循了一个叫做NSLocking的协议,所以大部分锁都是有如下两个方法的:

@protocol NSLocking

- (void)lock;
- (void)unlock;

@end

但是OCFoundation框架是不开源的,那难道我们的探索就止步于此了吗?有没有其他的方式来探索呢?

有的,我们可以通过 swiftFoundation框架的开源源码来看看锁是如何封装实现的,说干就干!👉源码戳这里

  • NSLock

swift中的NSLocking协议

swiftFoundation的源码工程中搜索NSLocking协议,发现这里针对不同的平台进行了一些设置和初始化工作,很明显可以看出来是对pthread的封装。

还有构造方法(init)、析构方法(deinit)、加锁(lock)、解锁(unlock)的方法定义,如下:

常规方法定义

  • 构造方法init()就是调用了pthreadpthread_mutex_init(mutex, nil)方法
  • 析构方法deinit就是调用了pthreadpthread_mutex_destroy(mutex)方法
  • 加锁方法 lock()就是调用了pthreadpthread_mutex_lock(mutex)方法
  • 解锁方法 unlock()就是调用了pthreadpthread_mutex_unlock(mutex)方法

通过NSLockNSRecursiveLock方法的对比,发现它们的lock()方法都是一样的,都调用了pthread_mutex_lock(mutex),如下:

  • NSRecursiveLock
NSLock和NSRecursiveLock的对比

那为什么NSRecursiveLock就可以支持可递归加锁呢?why ?为什么呢!请继续往下看:

NSLock和NSRecursiveLock的对比

NSRecursiveLock方法支持可递归加锁,原因就是图中所示,在init方法中,通过pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))进行了递归的设置,这一波操作真是很细节,非常的人性化的设计,着实到位👍(ps:哈哈)

  • NSCondition

NSCondition

通过搜索,发现NSCondition方法也是对pthread进行了封装,就是多了个对 cond的处理,除了进行pthread_mutex互斥处理外,还对pthread_cond进行了处理,同时提供了waitsignalbroadcase等方法,如下图所示:
wait、signal、broadcase等方法

  • NSConditionLock

NSConditionLock

NSConditionLock方法,里面没有直接对进行pthread_mutex进行封装,只是属性里面使用了NSCondition类型的_cond属性和一个_swift_CFThreadRef类型的属性,通过这两个属性,实现加锁和线程方面的相关处理。

  • NSCondition里面是包含了对pthread_mutex的封装
  • _swift_CFThreadRef其实是等于pthread_t
    _swift_CFThreadRef

2. NSLocationLock分析

先来看看下面👇的代码,猜猜打印的顺序:

NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [conditionLock lockWhenCondition:1];
        NSLog(@"线程 1");
        [conditionLock unlockWithCondition:0];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        [conditionLock lockWhenCondition:2];
        sleep(0.1);
        NSLog(@"线程 2");
        [conditionLock unlockWithCondition:1];
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       [conditionLock lock];
       NSLog(@"线程 3");
       [conditionLock unlock];
    });
  • 代码运行结果
NSConditionLock运行结果

这里线程 2 一定比线程 1先执行,因为NSConditionLock 锁,⼀旦⼀个线程获得锁,其他线程⼀定会等待,在初始化的时候设置了条件2,所以线程 2优先级高于线程 1

  • [xxxx lock]:表示 xxx期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执⾏此⾏以下代码,如果已经有其他线程获得锁(可能是条件锁,或者⽆条件锁),则等待,直⾄其他线程解锁
  • [xxx lockWhenCondition:A条件]:表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进⼊代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直⾄它解锁。
  • [xxx unlockWithCondition:A条件]: 表示释放锁,同时把内部的condition设置为A条件
  • return = [xxx lockWhenCondition:A条件 beforeDate:A时间]:表示如果被锁定(没获得锁),并超过该时间则不再阻塞线程。但是注意:返回的值是NO,它没有改变锁的状态,这个函数的⽬的在于可以实现两种状态下的处理
  • 所谓的condition就是整数,内部通过整数⽐较条件
  1. 线程 1 调⽤[NSConditionLock lockWhenCondition:],此时此刻因为不满⾜当前条件,所以会进⼊ waiting状态,当前进⼊到waiting时,会释放当前的互斥锁。
  2. 此时当前的线程 3 调⽤[NSConditionLock lock:],本质上是调⽤[NSConditionLock lockBeforeDate:],这⾥不需要⽐对条件值,所以线程 3会打印。
  3. 接下来线程 2 执⾏[NSConditionLock lockWhenCondition:],因为满⾜条件值,所以线程2会打印,打印完成后会调⽤[NSConditionLock unlockWithCondition:],这个时候把value设置为 1,并发送boradcast, 此时线程 1接收到当前的信号,唤醒执⾏并打印。
  4. ⾃此当前打印为 线程 3 --> 线程 2 --> 线程 1
  5. [NSConditionLock lockWhenCondition:]:这⾥会根据传⼊的 condition值和value值进⾏对⽐,如果不相等,这⾥就会阻塞,进⼊线程池,否则的话就继续代码执⾏。
  6. [NSConditionLock unlockWithCondition:]: 这⾥会先更改当前的value值,然后进⾏⼴播,唤醒当前的线程。

3.总结

  • 以上的锁,其实都是对pthread封装,你如果对pthread比较熟悉,能直接用就直接用,不能就用这些封装的就可以
  • 使用锁的时候,根据自己的业务场景来合理的使用互斥锁、递归锁、条件锁

更多内容持续更新

🌹 喜欢就点个赞吧👍🌹

🌹 觉得有收获的,可以来一波,收藏+关注,评论 + 转发,以免你下次找不到我😁🌹

🌹欢迎大家留言交流,批评指正,互相学习😁,提升自我🌹

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容