上一篇大概介紹了一下鎖的分類
這篇我們就解讀一下第一個鎖 NSLock
NSLock是Foundation提供的類,NSLock的API很少也很簡單欺抗。常用的就幾個方法
備注:深入解讀 NSLock則需要 Foundation 源碼, 但是這里公開的Foundation框架中只有NSObject的實(shí)現(xiàn)绽淘。假如我們想要查看NSString,NSArray居兆,NSRunLoop瘦黑,NSThread等Foundation這些類,是沒有源碼的植锉。雖然通過匯編語言辫樱,一步步的跟蹤也可以查看。但是匯編太過于晦澀難懂俊庇,所以這里推薦一個GNUstep狮暑。
GNUstep是GUN計劃的項(xiàng)目之一,它將Cocoa的OC庫重新開源實(shí)現(xiàn)了一遍辉饱,并且開源出來了搬男。雖然GNUstep不是蘋果官方的源碼,是GNU計劃寫的彭沼,但是還是具有一定參考價值的缔逛。
GNUstep源碼下載地址:http://www.gnustep.org/resources/downloads.php
一. NSLock 互斥鎖 不能多次調(diào)用 lock方法,會造成死鎖 ,遵循 NSLocking 協(xié)議。進(jìn)行加鎖、解鎖
@protocol NSLocking
(void)lock;//加鎖
(void)unlock;//解鎖
@end
NSLock實(shí)現(xiàn)了NSLocking協(xié)議:
@interface NSLock : NSObject {
@private
void *_priv;
}
// 嘗試獲取鎖,獲取到返回YES齐媒,獲取不到返回NO
(BOOL)tryLock;
// 在指定時間點(diǎn)之前獲取鎖味抖,能夠獲取返回YES,獲取不到返回NO
(BOOL)lockBeforeDate:(NSDate *)limit;
// 鎖名稱,如果使用鎖出現(xiàn)異常,輸出的log中會有
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
-tryLock:如果能獲取到鎖返回YES,如果獲取不到鎖返回NO匪补,但不會使線程進(jìn)入休眠,會繼續(xù)向下執(zhí)行
-lockBeforeDate::如果鎖已被鎖定烂翰,在指定的時間點(diǎn)之前線程進(jìn)入休眠等待鎖釋放夯缺。如果在時間點(diǎn)之前鎖被釋放了,線程立即被喚醒獲得鎖甘耿,該函數(shù)會返回YES踊兜,繼續(xù)執(zhí)行任務(wù),不會一直休眠等到那個時間點(diǎn)佳恬。如果等到時間點(diǎn)還沒有獲得鎖會返回NO捏境,并繼續(xù)執(zhí)行任務(wù)
NSLock是非遞歸鎖,不能重入毁葱,否則會發(fā)生死鎖:
上代碼~~~
可以看到上面的代碼最終只打印了testLock1垫言,其他的幾個打印不會去執(zhí)行。因?yàn)?testLock1被鎖了之后倾剿,還沒有調(diào)用解鎖就執(zhí)行了testLock2筷频。這個時候去lock 但是鎖獲取不到就休眠等待蚌成,直到testLock1 unlock解鎖之后才會繼續(xù)執(zhí)行,但是這個時候testLock2 不執(zhí)行完凛捏, testLock1 里面的代碼也就被卡著不能繼續(xù)担忧。
注意:
-lock和-unlock必須在相同的線程調(diào)用,也就是說坯癣,他們必須在同一個線程中成對調(diào)用瓶盛,否則會產(chǎn)生未知結(jié)果。
官方文檔原文:Unlocking a lock from a different thread can result in undefined behavior.
可以閱讀 GNUstep 的 Foundation (參考),也可以自行下載 swift 版本的 Foundation 源碼分析
通過源碼可知驗(yàn)證 NSLock 是對 pthread 中互斥鎖 的封裝示罗。
其他都好理解惩猫,這里列一下 timedLock() 的實(shí)現(xiàn)流程:
1、設(shè)定超時時間鹉勒,進(jìn)入while循環(huán)。
2吵取、pthread_cond_timedwait()在本次循環(huán)中計時等待禽额,線程進(jìn)入休眠
3、等待超時皮官,直接返回 false脯倒;
4、如果等待沒有超時捺氢,期間鎖被釋放藻丢,線程會被喚醒,再次嘗試獲取鎖 pthread_mutex_trylock(),如果獲取成功返回true
5摄乒、即沒有超時悠反,被喚醒后也沒有成功獲取到鎖(被其他線程搶先獲得鎖),重新計算超時時間進(jìn)入下一次while循環(huán)
NSLock 坑(網(wǎng)上看的例子,解讀一下)
代碼先執(zhí)行第 38 行,然后執(zhí)行第 31 行,此時鎖被 lock,然后繼續(xù)執(zhí)行 if 里面,因?yàn)槔锩嬗?testMethod(value-1),所以又開始調(diào)用[lock lock]方法,此時的鎖一直處于 lock 狀態(tài),if 還沒執(zhí)行完,沒有 unlock,所以NSlog 只會打印一次