在編程中,引入了對象互斥鎖的概念,來保證共享數據操做的完整性。每一個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。對於互斥鎖,若是資源已經被佔用,資源申請者只能進入睡眠狀態。編程
互斥鎖又分爲遞歸鎖和非遞歸鎖。swift
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//靜態初始化互斥量
int pthread_mutex_init(pthread_mutex_t*mutex,pthread_mutexattr_t*attr);//動態初始化互斥量
int pthread_mutex_destory(pthread_mutex_t*mutex);//註銷互斥量
複製代碼
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t*mutex);
複製代碼
pthread_mutex_trylock
當互斥量已經被鎖住時調用該函數將返回錯誤代碼EBUSY
,若是當前互斥量沒有被鎖,則會執行和pthread_mutex_lock
同樣的效果。pthread_mutex_unlock
對互斥量進行解鎖。咱們在swift
版本開源的CoreFoundation
框架下咱們能夠看到關於NSLock
的完整定義。安全
注意:如下代碼只摘取了關鍵部分,其餘兼容性代碼已被省略。bash
public override init() {
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
}
複製代碼
deinit {
pthread_mutex_destroy(mutex)
}
複製代碼
open func lock() {
pthread_mutex_lock(mutex)
}
複製代碼
open func unlock() {
pthread_mutex_unlock(mutex)
}
複製代碼
open func lock(before limit: Date) -> Bool {
if pthread_mutex_trylock(mutex) == 0 {
return true
}
return timedLock(mutex: mutex, endTime: limit, using: timeoutCond, with:
}
複製代碼
這個函數的主要做用就是在某一個時間點以前不斷的嘗試加鎖,主要作了下面幾件事情。框架
pthread_mutex_trylock
嘗試加鎖,若是加鎖成功則直接返回true
。timedLock
經過一層while
循環調用pthread_mutex_trylock
不斷嘗試加鎖。若是失敗失敗則返回false
,若是成功則返回true
。經過上述代碼咱們能夠發現NSLock
底層呢,其實也就是基於pthread_mutex
進行了一次面向對象的封裝。由於他是比pthread_mutex
更高一級的API,因此在性能方面呢比pthread_mutex
稍微差一點。ide
NSCondition
和NSLock
同樣都是基於pthread_mutex
進行面向對象分裝的一個非遞歸的互斥鎖,在swift
版本開源的CoreFoundation
框架下咱們一樣能夠找到相關的開源代碼。函數
條件對象既充當給定線程中的鎖又充當檢查點。鎖在測試條件並執行條件觸發的任務時保護您的代碼。檢查點行爲要求條件在線程繼續執行其任務以前爲真。條件不成立時,線程將阻塞。它保持阻塞狀態,直到另外一個線程向條件對象發出信號爲止。性能
注意:如下代碼只摘取了關鍵部分,其餘兼容性代碼已被省略。測試
public override init() {
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
}
複製代碼
deinit {
pthread_mutex_destroy(mutex)
}
複製代碼
open func lock() {
pthread_mutex_lock(mutex)
}
複製代碼
open func unlock() {
pthread_mutex_unlock(mutex)
}
複製代碼
它在NSLock
的基礎上又增長了些新的功能。ui
open func wait() {
pthread_cond_wait(cond, mutex)
}
複製代碼
阻塞當前線程,直到接收到條件信號爲止。
open func wait(until limit: Date) -> Bool {
guard var timeout = timeSpecFrom(date: limit) else {
return false
}
return pthread_cond_timedwait(cond, mutex, &timeout) == 0
}
複製代碼
阻塞當前線程,直到接收到條件信號或達到指定的時間限制爲止。
open func signal() {
pthread_cond_signal(cond)
}
複製代碼
咱們經過查閱pthread
開源代碼以下
/* * Signal a condition variable, waking only one thread. */
PTHREAD_NOEXPORT_VARIANT int pthread_cond_signal(pthread_cond_t *ocond) {
return _pthread_cond_signal(ocond, false, MACH_PORT_NULL);
}
複製代碼
發信號通知條件變量,僅喚醒一個線程。
open func broadcast() {
pthread_cond_broadcast(cond) // wait signal
}
複製代碼
咱們在pthread
的開源代碼中找到這麼代碼。
/* * Signal a condition variable, waking up all threads waiting for it. */
PTHREAD_NOEXPORT_VARIANT int pthread_cond_broadcast(pthread_cond_t *ocond) {
return _pthread_cond_signal(ocond, true, MACH_PORT_NULL);
}
複製代碼
蘋果的註釋告訴咱們這是一個(發信號通知條件變量,喚醒全部等待它的線程。)的函數。
使用NSConditionLock
對象,能夠確保線程僅在知足特定條件時才能獲取鎖。 一旦得到了鎖並執行了臨界區的代碼,線程就能夠放棄該鎖並將關聯條件設置爲新的條件。 條件自己是任意的:您能夠根據應用程序的須要定義它們。
NSConditionLock
是NSCondition
的升級版,其內部持有了一個條件鎖NSCondition
。
internal var _cond = NSCondition()//內存持有的條件鎖
internal var _value: Int//條件變量
internal var _thread: _swift_CFThreadRef?//當前的線程
複製代碼
open func lock() {
let _ = lock(before: Date.distantFuture)
}
複製代碼
其內部是調用lock(before limit: Date) -> Bool
來完成的。
open func lock(before limit: Date) -> Bool {
_cond.lock()
while _thread != nil {
if !_cond.wait(until: limit) {
_cond.unlock()
return false
}
}
_thread = pthread_self()
_cond.unlock()
return true
}
複製代碼
這個函數作了如下幾件事情。
_thread
屬性,而後對條件鎖進行一個unlock
解鎖的操做。open func unlock() {
_cond.lock()
#if os(Windows)
_thread = INVALID_HANDLE_VALUE
#else
_thread = nil
#endif
_cond.broadcast()
_cond.unlock()
}
複製代碼
首先調用NSCondition
的lock
方法給接下來的臨界區加鎖。
對_thread
屬性置爲nil
。
發信號通知條件變量,喚醒全部等待它的線程。
調用NSCondition
的unlock
方法解鎖。
open func lock(whenCondition condition: Int) {
let _ = lock(whenCondition: condition, before: Date.distantFuture)
}
複製代碼
其內部調用了lock(whenCondition condition: Int, before limit: Date) -> Bool
open func lock(whenCondition condition: Int, before limit: Date) -> Bool {
_cond.lock()
while _thread != nil || _value != condition {
if !_cond.wait(until: limit) {
_cond.unlock()
return false
}
}
#if os(Windows)
_thread = GetCurrentThread()
#else
_thread = pthread_self()
#endif
_cond.unlock()
return true
}
複製代碼
這裏和上面的lock
實現原理差很少,只是多了一個_value != condition
條件的判斷。
open func unlock(withCondition condition: Int) {
_cond.lock()
#if os(Windows)
_thread = INVALID_HANDLE_VALUE
#else
_thread = nil
#endif
_value = condition
_cond.broadcast()
_cond.unlock()
}
複製代碼
這裏的實現邏輯和unlock
的相同,只是多了一步修改條件變量的步驟_value = condition
。
open func tryLock(whenCondition condition: Int) -> Bool {
return lock(whenCondition: condition, before: Date.distantPast)
}
複製代碼
和lockWhenCondition
同樣內部都是經過調用lock(whenCondition condition: Int, before limit: Date) -> Bool
實現的。
咱們有幸獲得了NSRecursiveLock
底層源碼,咱們一塊兒來看看它是如何實現遞歸的。
注意:如下代碼只截取了關鍵部分。
public override init() {
super.init()
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
}
複製代碼
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
這是一句關鍵性的代碼,經過這個type
來區分是不是遞歸鎖。#define PTHREAD_MUTEX_NORMAL 0 //普通非遞歸鎖
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2 //遞歸鎖
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL //默認是普通非遞歸鎖
複製代碼
open func lock() {
pthread_mutex_lock(mutex)
}
複製代碼
底層是調用了pthread
加鎖函數。
open func unlock() {
pthread_mutex_unlock(mutex)
}
複製代碼
deinit {
pthread_mutex_destroy(mutex)
}
複製代碼
NSRecursiveLock
底層就是對pthread_mutex
的封裝,經過設置標識來區分是否爲遞歸鎖。
在pthread
咱們經過搜索PTHREAD_MUTEX_RECURSIVE
遞歸類型可找到以下代碼。
PTHREAD_ALWAYS_INLINE
static inline bool
_pthread_mutex_is_recursive(_pthread_mutex *mutex)
{
return (mutex->mtxopts.options.type == PTHREAD_MUTEX_RECURSIVE);
}
複製代碼
經過類型匹配,返回當前的鎖是不是遞歸鎖。
咱們在經過從pthread_mutex_lock
以此查看源碼,看是否能找到調用_pthread_mutex_is_recursive
的地方。
pthread_mutex_lock->_pthread_mutex_firstfit_lock->_pthread_mutex_firstfit_lock_slow->_pthread_mutex_lock_handle_options
複製代碼
在_pthread_mutex_lock_handle_options中,找到了這個函數。
static int
_pthread_mutex_lock_handle_options(_pthread_mutex *mutex, bool trylock,
uint64_t *tidaddr)
{
if (mutex->mtxopts.options.type == PTHREAD_MUTEX_NORMAL) {
// NORMAL does not do EDEADLK checking
return 0;
}
uint64_t selfid = _pthread_selfid_direct();
if (os_atomic_load(tidaddr, relaxed) == selfid) {
if (_pthread_mutex_is_recursive(mutex)) {
if (mutex->mtxopts.options.lock_count < USHRT_MAX) {
mutex->mtxopts.options.lock_count += 1;
return mutex->mtxopts.options.lock_count;
} else {
return -EAGAIN;
}
} else if (trylock) { /* PTHREAD_MUTEX_ERRORCHECK */
// <rdar://problem/16261552> as per OpenGroup, trylock cannot
// return EDEADLK on a deadlock, it should return EBUSY.
return -EBUSY;
} else { /* PTHREAD_MUTEX_ERRORCHECK */
return -EDEADLK;
}
}
// Not recursive, or recursive but first lock.
return 0;
}
複製代碼
在這裏咱們發現若是是遞歸鎖,那麼這把鎖維護了一個lock_count
的引用計數。每加一次鎖都會對引用計數執行+1的操做。
這裏截取主要邏輯
int
_pthread_mutex_firstfit_lock_slow(_pthread_mutex *mutex, bool trylock)
{
res = _pthread_mutex_lock_handle_options(mutex, trylock, tidaddr);
if (res > 0) {
recursive = 1;
res = 0;
goto out;
} else if (res < 0) {
res = -res;
goto out;
}
}
複製代碼
接下來會跳轉到out
代碼塊中。
out:
#if PLOCKSTAT
if (res == 0) {
PLOCKSTAT_MUTEX_ACQUIRE((pthread_mutex_t *)mutex, recursive, 0);
} else {
PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, res);
}
#endif
return res;
}
複製代碼
咱們發現當res
爲0,也就是肯定是遞歸鎖的時候,會對當前的鎖進行一次持有。能夠理解爲對鎖的引用計數加1。
pthread_mutex_unlock->_pthread_mutex_firstfit_unlock_slow->_pthread_mutex_firstfit_unlock_updatebits->_pthread_mutex_unlock_handle_options
複製代碼
同理,咱們發現當咱們在調用解鎖的函數時lock_count
會作一次-1的操做。再判斷是遞歸鎖和lock_countda
大於0時返回1。
static int
_pthread_mutex_unlock_handle_options(_pthread_mutex *mutex, uint64_t *tidaddr)
{
if (mutex->mtxopts.options.type == PTHREAD_MUTEX_NORMAL) {
// NORMAL does not do EDEADLK checking
return 0;
}
uint64_t selfid = _pthread_selfid_direct();
if (os_atomic_load(tidaddr, relaxed) != selfid) {
return -EPERM;
} else if (_pthread_mutex_is_recursive(mutex) &&
--mutex->mtxopts.options.lock_count) {
return 1;
}
return 0;
}
複製代碼
主要的解鎖邏輯
static inline int
_pthread_mutex_firstfit_unlock_updatebits(_pthread_mutex *mutex,
uint32_t *flagsp, uint32_t **mutexp, uint32_t *lvalp, uint32_t *uvalp)
{
int res = _pthread_mutex_unlock_handle_options(mutex, tidaddr);
if (res > 0) {
// Valid recursive unlock
if (flagsp) {
*flagsp = flags;
}
PLOCKSTAT_MUTEX_RELEASE((pthread_mutex_t *)mutex, 1);
return 0;
} else if (res < 0) {
PLOCKSTAT_MUTEX_ERROR((pthread_mutex_t *)mutex, -res);
return -res;
}
return 0;
}
複製代碼
當_pthread_mutex_unlock_handle_options
返回的結果大於0的時候進行正常的遞歸解鎖操做。能夠理解爲鎖的引用計數加1。
遞歸鎖咱們能夠簡單理解爲是一個OC對象,它擁有一個引用計數的屬性,當咱們進行多層次的遞歸加鎖時,引用計數會加一,每釋放一層鎖的時候,引用計數會加一,當引用計數爲0的時候,這個遞歸鎖就會被釋放。等待下一個線程對他的持有。
這裏介紹了基於pthread_mutex
實現的遞歸鎖以及非遞歸鎖。
非遞歸鎖裏面NSLock
是基於pthread_mutex
實現的。
條件鎖NSCondition
也是基於pthread_mutex
實現的,基礎功能和NSLock
類似。在NSLock
的基礎上增長了一些功能。
條件鎖NSConditionLock
是基於NSCondition
實現的,在其基礎上進一步的增長了一下其餘功能。
遞歸鎖NSRecursiveLock
也是基於pthread_mutex
實現的。運用了引用計數的思想。經過維護引用計數來達到一個加解鎖平衡的狀態。
以上代碼的分析來自swift版本的CoreFoundation
和pthread
。都可在蘋果的開源代碼庫下載到。