鎖
- 我們在使用多線程的時候多個線程可能會訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全等問題,這時候就需要我們保證每次只有一個線程訪問這一塊資源,鎖 應運而生。
- 鎖是最常用的同步工具:一段代碼段在同一個時間只能允許被一個線程訪問,比如一個線程A進入加鎖代碼之后由于已經(jīng)加鎖,另一個線程B就無法訪問,只有等待前一個線程A執(zhí)行完加鎖代碼后解鎖,B線程才能訪問加鎖代碼。
開篇
講iOS 鎖 自然繞不開大神ibireme前段時間的一篇文章 《不再安全的 OSSpinLock》
ibireme 文中論述了OSSpinLock為什么已經(jīng)不再是線程安全,想具體了解的童鞋可以去大神的原文了解,我就在下面簡要介紹一下:
(課外知識,借鑒!)深入理解 iOS 開發(fā)中的鎖
OSSpinLock 不再安全,主要原因發(fā)生在低優(yōu)先級線程拿到鎖時,高優(yōu)先級線程進入忙等(busy-wait)狀態(tài),消耗大量 CPU 時間,從而導致低優(yōu)先級線程拿不到 CPU 時間,也就無法完成任務并釋放鎖。這種問題被稱為優(yōu)先級反轉(zhuǎn)。
新版 iOS 中,系統(tǒng)維護了 5 個不同的線程優(yōu)先級/QoS: background,utility,default,user-initiated,user-interactive。
高優(yōu)先級線程始終會在低優(yōu)先級線程前執(zhí)行,一個線程不會受到比它更低優(yōu)先級線程的干擾。
這種線程調(diào)度算法會產(chǎn)生潛在的優(yōu)先級反轉(zhuǎn)問題,從而破壞了 spin lock。
為什么忙等會導致低優(yōu)先級線程拿不到時間片?這還得從操作系統(tǒng)的線程調(diào)度說起。
現(xiàn)代操作系統(tǒng)在管理普通線程時,通常采用時間片輪轉(zhuǎn)算法(Round Robin,簡稱 RR)。每個線程會被分配一段時間片(quantum),通常在 10-100 毫秒左右。當線程用完屬于自己的時間片以后,就會被操作系統(tǒng)掛起,放入等待隊列中,直到下一次被分配時間片。
OSSpinLock 自旋鎖 實現(xiàn)機制:忙等 操作重點:原子操作
需導入頭文件:
#import <libkern/OSAtomic.h>
OS_SPINLOCK_INIT: 默認值為 0,在 locked 狀態(tài)時就會大于 0,unlocked狀態(tài)下為 0
OSSpinLockLock(&oslock):上鎖,參數(shù)為 OSSpinLock 地址
OSSpinLockUnlock(&oslock):解鎖,參數(shù)為 OSSpinLock 地址
OSSpinLockTry(&oslock):嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"準備上鎖");
OSSpinLockLock(&oslock);
OSSpinLockUnlock(&oslock);
NSLog(@"解鎖成功");
});
dispatch_semaphore 信號量
** 實現(xiàn)原理:不是使用忙等,而是阻塞線程并睡眠,主動讓出時間片,需要進行上下文切換**
主動讓出時間片并不總是代表效率高。
讓出時間片會導致操作系統(tǒng)切換到另一個線程,這種上下文切換通常需要 10 微秒左右,而且至少需要兩次切換。
如果等待時間很短,比如只有幾個微秒,忙等就比線程睡眠更高效。(系統(tǒng)任務少,下次分配時間片的間隔也不長的情況下,忙等的線程可以在較短的時間后就可以再次分配到時間片 作者就是在比較這個等待的時間間隔和讓出時間片 付出的兩次切換上下文花費的時間做比較)
dispatch_semaphore_create(1): 傳入值必須 >=0, 若傳入為 0 則阻塞線程并等待timeout,時間到后會執(zhí)行其后的語句
dispatch_semaphore_wait(signal, overTime):可以理解為 lock,會使得 signal 值 -1
dispatch_semaphore_signal(signal):可以理解為 unlock,會使得 signal值 +1
dispatch_semaphore_t signal = dispatch_semaphore_create(1); //傳入值必須 >=0, 若傳入為0則阻塞線程并等待timeout,時間到后會執(zhí)行其后的語句
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@" 等待");
dispatch_semaphore_wait(signal, overTime); //signal 值 -1
dispatch_semaphore_signal(signal); //signal 值 +1
NSLog(@"發(fā)送信號");
});
對于dispatch_semaphore網(wǎng)上有個很形象的例子:
停車場剩余4個車位,那么即使同時來了四輛車也能停的下。如果此時來了五輛車,那么就有一輛需要等待。
信號量的值(signal)就相當于剩余車位的數(shù)目,
dispatch_semaphore_wait 函數(shù)就相當于來了一輛車,
dispatch_semaphore_signal 就相當于走了一輛車。
停車位的剩余數(shù)目在初始化的時候就已經(jīng)指明了(dispatch_semaphore_create(long value)),
調(diào)用一次 dispatch_semaphore_signal,剩余的車位就增加一個;
調(diào)用一次dispatch_semaphore_wait 剩余車位就減少一個;
當剩余車位為 0 時,再來車(即調(diào)用 dispatch_semaphore_wait)就只能等待。
有可能同時有幾輛車等待一個停車位。有些車主沒有耐心,給自己設定了一段等待時間,這段時間內(nèi)等不到停車位就走了,如果等到了就開進去停車。而有些車主就想把車停在這,所以就一直等下去。
pthread 表示 POSIX thread 定義了一組跨平臺的線程相關的 API
pthread_mutex 互斥鎖 實現(xiàn)原理:不是使用忙等,而是阻塞線程并睡眠,主動讓出時間片,需要進行上下文切換
ibireme 在《不再安全的 OSSpinLock》這篇文章中提到性能最好的 OSSpinLock 已經(jīng)不再是線程安全的并把自己開源項目中的 OSSpinLock 都替換成了 pthread_mutex。
了解互斥鎖POSIX
POSIX和dispatch_semaphore_t很像,但是完全不同。
POSIX是Unix/Linux平臺上提供的一套條件互斥鎖的API。
新建一個簡單的POSIX互斥鎖,引入頭文件#import <pthread.h>聲明
初始化一個pthread_mutex_t的結(jié)構。
使用pthread_mutex_lock和pthread_mutex_unlock函數(shù)。
調(diào)用pthread_mutex_destroy來釋放該鎖的數(shù)據(jù)結(jié)構。
條件變量
POSIX還可以創(chuàng)建條件鎖,提供了和NSCondition一樣的條件控制,初始化互斥鎖同時使用pthread_cond_init來初始化條件數(shù)據(jù)結(jié)構,
// 初始化
int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *attr);
// 等待(會阻塞)
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut);
// 定時等待
int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mut, const struct timespec *abstime);
// 喚醒
int pthread_cond_signal (pthread_cond_t *cond);
// 廣播喚醒
int pthread_cond_broadcast (pthread_cond_t *cond);
// 銷毀
int pthread_cond_destroy (pthread_cond_t *cond);
POSIX還提供了很多函數(shù),有一套完整的API,包含Pthreads線程的創(chuàng)建控制等等,非常底層,可以手動處理線程的各個狀態(tài)的轉(zhuǎn)換即管理生命周期,甚至可以實現(xiàn)一套自己的多線程,感興趣的可以繼續(xù)深入了解。推薦一篇詳細文章,但不是基于iOS的,是基于Linux的,但是介紹的非常詳細 Linux 線程鎖詳解
使用需導入頭文件:#import <pthread.h>
互斥鎖的常見用法如下:
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // 定義鎖的屬性
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr) // 創(chuàng)建鎖
pthread_mutex_lock(&mutex); // 申請鎖
// 臨界區(qū)
pthread_mutex_unlock(&mutex); // 釋放鎖
static pthread_mutex_t pLock;
pthread_mutex_init(&pLock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"準備上鎖");
pthread_mutex_lock(&pLock);
sleep(3);
pthread_mutex_unlock(&pLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"準備上鎖");
pthread_mutex_lock(&pLock);
pthread_mutex_unlock(&pLock);
});
- pthread_mutex 中也有個pthread_mutex_trylock(&pLock),和上面提到的 OSSpinLockTry(&oslock)區(qū)別在于,前者可以加鎖時返回的是 0,否則返回一個錯誤提示碼;后者返回的 YES和NO
**由于 pthread_mutex 有多種類型,可以支持遞歸鎖等,因此在申請加鎖時,需要對鎖的類型加以判斷,這也就是為什么它和信號量的實現(xiàn)類似,但效率略低的原因。 **
pthread_mutex(recursive) 遞歸鎖 <pthread_mutex>
經(jīng)過上面幾種例子,我們可以發(fā)現(xiàn):
加鎖后只能有一個線程訪問該對象,后面的線程需要排隊,
并且 lock 和 unlock 是對應出現(xiàn)的,
同一線程多次 lock 是不允許的,
而遞歸鎖允許同一個線程在未釋放其擁有的鎖時反復對該鎖進行加鎖操作。
static pthread_mutex_t pLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr并且給它賦予默認
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //設置鎖類型,這邊是設置為遞歸鎖
pthread_mutex_init(&pLock, &attr);
pthread_mutexattr_destroy(&attr); //銷毀一個屬性對象,在重新進行初始化之前該結(jié)構不能重新使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
pthread_mutex_lock(&pLock);
if (value > 0) {
RecursiveBlock(value - 1);
}
pthread_mutex_unlock(&pLock);
};
RecursiveBlock(5);
});
上面的代碼如果我們用
- 互斥鎖 pthread_mutex_init(&pLock, NULL) 初始化會出現(xiàn)死鎖的情況,
- 遞歸鎖pthread_mutexattr_init(&attr);能很好的避免這種情況的死鎖;
一般情況下,一個線程只能申請一次鎖,也只能在獲得鎖的情況下才能釋放鎖,多次申請鎖或釋放未獲得的鎖都會導致崩潰。假設在已經(jīng)獲得鎖的情況下再次申請鎖,線程會因為等待鎖的釋放而進入睡眠狀態(tài),因此就不可能再釋放鎖,從而導致死鎖。
pthread_rwlock 讀寫鎖
在對文件進行操作的時候,寫操作是排他的,一旦有多個線程對同一個文件進行寫操作,后果不可估量
但讀是可以的,多個線程讀取時沒有問題的。
- 當讀寫鎖被一個線程以讀模式占用的時候,寫操作的其他線程會被阻塞,讀操作的其他線程還可以繼續(xù)進行。
- 當讀寫鎖被一個線程以寫模式占用的時候,寫操作的其他線程會被阻塞,讀操作的其他線程也被阻塞。
// 初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
// 讀模式
pthread_rwlock_wrlock(&lock);
// 寫模式
pthread_rwlock_rdlock(&lock);
// 讀模式或者寫模式的解鎖
pthread_rwlock_unlock(&lock);
NSLock 普通鎖
** 實質(zhì):NSLock 只是在內(nèi)部封裝了一個 pthread_mutex,屬性為 PTHREAD_MUTEX_ERRORCHECK,它會損失一定性能換來錯誤提示。**
NSLock 是 Objective-C 以對象的形式暴露給開發(fā)者的一種鎖,它的實現(xiàn)非常簡單,通過宏,定義了 lock 方法:
#define MLOCK \
- (void) lock\
{\
int err = pthread_mutex_lock(&_mutex);\
// 錯誤處理 ……
}
這里使用宏定義的原因是,OC 內(nèi)部還有其他幾種鎖,他們的 lock 方法都是一模一樣,僅僅是內(nèi)部 pthread_mutex 互斥鎖的類型不同。通過宏定義,可以簡化方法的定義。
NSLock 比 pthread_mutex 略慢的原因在于它需要經(jīng)過方法調(diào)用,同時由于緩存的存在,多次方法調(diào)用不會對性能產(chǎn)生太大的影響。
lock、unlock:不多做解釋,和上面一樣
trylock:能加鎖返回 YES 并執(zhí)行加鎖操作,相當于 lock,反之返回 NO
lockBeforeDate:這個方法表示會在傳入的時間內(nèi)嘗試加鎖,若能加鎖則執(zhí)行加鎖操作并返回 YES,反之返回 NO
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"嘗試加鎖");
[lock lock];
[lock unlock];
NSLog(@"解鎖成功");
});
NSCondition 條件鎖 <NSLocking>
NSCondition 其實是封裝了一個互斥鎖和條件變量, 它把前者的 lock 方法和后者的 wait/signal 統(tǒng)一在 NSCondition 對象中,暴露給使用者:
- (void) signal {
pthread_cond_signal(&_condition);
}
其實這個函數(shù)是通過宏來定義的,展開后就是這樣
- (void) lock {
int err = pthread_mutex_lock(&_mutex);
}
它的加解鎖過程與 NSLock 幾乎一致,理論上來說耗時也應該一樣(實際測試也是如此)。在圖中顯示它耗時略長,我猜測有可能是測試者在每次加解鎖的前后還附帶了變量的初始化和銷毀操作
NSCondition 的底層是通過條件變量(condition variable) pthread_cond_t 來實現(xiàn)的。
條件變量有點像信號量,提供了線程阻塞與信號機制,因此可以用來阻塞某個線程,并等待某個數(shù)據(jù)就緒,隨后喚醒線程,比如常見的生產(chǎn)者-消費者模式。
wait:進入等待狀態(tài)
waitUntilDate::讓一個線程等待一定的時間
signal:喚醒一個等待的線程
broadcast:喚醒所有等待的線程
NSCondition *cLock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lock];
[cLock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[cLock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"喚醒一個等待的線程");
[cLock signal];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"喚醒所有等待的線程");
[cLock broadcast];
});
NSRecursiveLock 遞歸鎖 <NSLocking>
上文已經(jīng)說過,遞歸鎖也是通過 pthread_mutex_lock 函數(shù)來實現(xiàn),在函數(shù)內(nèi)部會判斷鎖的類型,如果顯示是遞歸鎖,就允許遞歸調(diào)用,僅僅將一個計數(shù)器加一,鎖的釋放過程也是同理。
NSRecursiveLock 與 NSLock 的區(qū)別在于內(nèi)部封裝的 pthread_mutex_t 對象的類型不同,前者的類型為 PTHREAD_MUTEX_RECURSIVE, 后者的類型為 PTHREAD_MUTEX_ERRORCHECK。
上面已經(jīng)大概介紹過了:
遞歸鎖可以被同一線程多次請求lock,而不會引起死鎖。這主要是用在循環(huán)或遞歸操作中
不使用遞歸鎖的造成死鎖:
NSLock *rLock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
[rLock lock];
if (value > 0) {
NSLog(@"線程%d", value);
RecursiveBlock(value - 1);
}
[rLock unlock];
};
RecursiveBlock(4);
});
這段代碼是一個典型的死鎖情況。
在我們的線程中,RecursiveMethod 是遞歸調(diào)用的。
所以每次進入這個 block 時,都會去加一次鎖,而從第二次開始,由于鎖已經(jīng)被使用了且沒有解鎖,所以它需要等待鎖被解除,這樣就導致了死鎖,線程被阻塞住了。
使用需導入頭文件:#import <pthread.h>
互斥鎖的常見用法如下:
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // 定義鎖的屬性
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr) // 創(chuàng)建鎖
pthread_mutex_lock(&mutex); // 申請鎖
// 臨界區(qū)
pthread_mutex_unlock(&mutex); // 釋放鎖
NSConditionLock 條件鎖 <NSLocking>
NSConditionLock 借助 NSCondition 來實現(xiàn),
它的本質(zhì)就是一個生產(chǎn)者-消費者模型。“條件被滿足”可以理解為生產(chǎn)者提供了新的內(nèi)容。
NSConditionLock 的內(nèi)部持有一個 NSCondition 對象,以及 _condition_value 屬性,在初始化時就會對這個屬性進行賦值:
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
* 相比于 NSLock 多了個 condition 參數(shù),我們可以理解為一個條件標示。
NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if([cLock tryLockWhenCondition:0]){
NSLog(@"線程1");
[cLock unlockWithCondition:1];
}else{
NSLog(@"失敗");
}
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:3];
NSLog(@"線程2");
[cLock unlockWithCondition:2];
});
//線程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:1];
NSLog(@"線程3");
[cLock unlockWithCondition:3];
});
* 我們在初始化 NSConditionLock 對象時,給了他的標示為 0
* 執(zhí)行 tryLockWhenCondition:時,我們傳入的條件標示也是 0,所 以線程1 加鎖成功
* 執(zhí)行 unlockWithCondition:時,這時候會把condition由 0 修改為 1
* 因為condition 修改為了 1, 會先走到 線程3,然后 線程3 又將 condition 修改為 3
* 最后 走了 線程2 的流程
從上面的結(jié)果我們可以發(fā)現(xiàn),NSConditionLock 還可以實現(xiàn)任務之間的依賴。
@synchronized 互斥鎖
這其實是一個 OC 層面的鎖, 主要是通過犧牲性能換來語法上的簡潔與可讀。
我們知道 @synchronized 后面需要緊跟一個 OC 對象,它實際上是把這個對象當做鎖來使用。
這是通過一個哈希表來實現(xiàn)的,OC 在底層使用了一個互斥鎖的數(shù)組(你可以理解為鎖池),通過對象去哈希值來得到對應的互斥鎖。
@synchronized 相信大家應該都熟悉,它的用法應該算這些鎖中最簡單的:
@synchronized 結(jié)構所做的事情跟鎖(lock)類似:它防止不同的線程同時執(zhí)行同一段代碼。但在某些情況下,相比于使用 NSLock 創(chuàng)建鎖對象、加鎖和解鎖來說,
@synchronized 用著更方便,可讀性更高。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (self) {
sleep(2);
}
});
延伸:
鎖是一種同步機制;為了防止同一資源被多個線程同時訪問引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全等問題
GCD線程阻斷dispatch_barrier_async/dispatch_barrier_sync
dispatch_barrier_async/dispatch_barrier_sync在一定的基礎上也可以做線程同步,會在線程隊列中打斷其他線程執(zhí)行當前任務,
也就是說只有用在并發(fā)的線程隊列中才會有效,因為串行隊列本來就是一個一個的執(zhí)行的,你打斷執(zhí)行一個和插入一個是一樣的效果。
兩個的區(qū)別是是否等待任務執(zhí)行完成。
- 注意:如果在當前線程調(diào)用dispatch_barrier_sync打斷會發(fā)生死鎖。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (self) {
sleep(2);
}
});
NSDistributedLock 分布式鎖 (MAC開發(fā)中的跨進程的分布式鎖)
NSDistributedLock是MAC開發(fā)中的跨進程的分布式鎖,底層是用文件系統(tǒng)實現(xiàn)的互斥鎖。
NSDistributedLock沒有實現(xiàn)NSLocking協(xié)議,所以沒有l(wèi)ock方法,取而代之的是非阻塞的tryLock方法。
NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"];
while (![lock tryLock])
{
sleep(1);
}
//do something
[lock unlock];
當執(zhí)行到do something時程序退出,程序再次啟動之后tryLock就再也不能成功了,陷入死鎖狀態(tài).其他應用也不能訪問受保護的共享資源。
在這種情況下,你可以使用breadLock方法來打破現(xiàn)存的鎖以便你可以獲取它。
但是通常應該避免打破鎖,除非你確定擁有進程已經(jīng)死亡并不可能再釋放該鎖。
了解死鎖
概念:死鎖是指兩個或兩個以上的進程(線程)在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進下去。
產(chǎn)生死鎖的4個必要條件
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內(nèi)某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。
2)請求和保持條件:指進程已經(jīng)保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)環(huán)路等待條件:指在發(fā)生死鎖時,必然存在一個進程——資源的環(huán)形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。
結(jié)束語
iOS的鎖有這么多,需要根據(jù)自己的需要選擇合適的鎖.但是線程安全是選擇是的首要標準!
網(wǎng)上有很多人都對iOS 的鎖進行了性能測試(只能作為參考),感興趣的可以去看看,我也推薦兩篇:
iOS同步對象性能對比(iOS鎖性能對比)iOS多線程-各種線程鎖的簡單介紹
參考資料
pthread_mutex_lock
ThreadSafety
Difference between binary semaphore and mutex
關于 @synchronized,這兒比你想知道的還要多
pthread_mutex_lock.c 源碼
[Pthread] Linux中的線程同步機制(二)--In Glibc
pthread的各種同步機制
pthread_cond_wait
Conditional Variable vs Semaphore
NSRecursiveLock Class Reference
Objective-C中不同方式實現(xiàn)鎖(二)
相關鏈接
https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000344.html
http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
http://engineering.postmates.com/Spinlocks-Considered-Harmful-On-iOS/
https://twitter.com/steipete/status/676851647042203648