iOS 十種線程鎖

鎖 是什么意思?

  • 我們在使用多線程的時候多個線程可能會訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全等問題,這時候就需要我們保證每次只有一個線程訪問這一塊資源,鎖 應(yīng)運(yùn)而生。
  • 這里順便提一下,上鎖的兩種方式trylock和lock使用場景:
當(dāng)前線程鎖失敗,也可以繼續(xù)其它任務(wù),用 trylock 合適
當(dāng)前線程只有鎖成功后,才會做一些有意義的工作,那就 lock,沒必要輪詢 trylock
注:以下大部分鎖都會提供trylock接口,不再作解釋

image

<準(zhǔn)備操作>

測試代碼
#define RHTICK   NSDate *startTime = [NSDate date];
#define RHTOCK   NSLog(@"==========Time: %f", -[startTime timeIntervalSinceNow]);
NSUInteger count = 1000*10000;//執(zhí)行一千萬次
RHTICK
for(int i=0; i<count; i++) {
加鎖
解鎖
}
RHTOCK
注:測試中執(zhí)行時間會波動,所以我取的平均值.

一、OSSpinLock (自旋鎖)

測試中效率最高的鎖, 不過經(jīng)YYKit作者確認(rèn), OSSpinLock已經(jīng)不再線程安全,OSSpinLock有潛在的優(yōu)先級反轉(zhuǎn)問題.不再安全的 OSSpinLock;

  • 0.097348s
需要導(dǎo)入頭文件
#import <libkern/OSAtomic.h>
// 初始化
 OSSpinLock spinLock = OS_SPINLOCK_INIT;
// 加鎖
OSSpinLockLock(&spinLock);
// 解鎖
OSSpinLockUnlock(&spinLock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
OSSpinLockTry(&spinLock)
/*
注:蘋果爸爸已經(jīng)在iOS10.0以后廢棄了這種鎖機(jī)制,使用os_unfair_lock 替換,
顧名思義能夠保證不同優(yōu)先級的線程申請鎖的時候不會發(fā)生優(yōu)先級反轉(zhuǎn)問題.
*/

二、os_unfair_lock(互斥鎖)

  • 0.171789s
需要導(dǎo)入頭文件
#import <os/lock.h>
// 初始化
 os_unfair_lock unfair_lock = OS_UNFAIR_LOCK_INIT;
// 加鎖
os_unfair_lock_lock(&unfair_lock);
// 解鎖
os_unfair_lock_unlock(&unfair_lock);
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
os_unfair_lock_trylock(&unfair_lock);
/*
注:解決不同優(yōu)先級的線程申請鎖的時候不會發(fā)生優(yōu)先級反轉(zhuǎn)問題.
不過相對于 OSSpinLock , os_unfair_lock性能方面減弱了許多.
*/

三、dispatch_semaphore (信號量)

  • 0.155043s
// 初始化
dispatch_semaphore_t semaphore_t = dispatch_semaphore_create(1);
// 加鎖
dispatch_semaphore_wait(semaphore_t,DISPATCH_TIME_FOREVER);
// 解鎖
dispatch_semaphore_signal(semaphore_t);
/*
注: dispatch_semaphore  其他兩個功能
1.還可以起到阻塞線程的作用.
2.可以實(shí)現(xiàn)定時器功能,這里不做過多介紹.
*/

四、pthread_mutex(互斥鎖)

  • 0.262592s
需要導(dǎo)入頭文件
#import <pthread/pthread.h>
// 初始化(兩種)
1.普通初始化
pthread_mutex_t mutex_t;
pthread_mutex_init(&mutex_t, NULL); 
2.宏初始化
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
// 嘗試加鎖,可以加鎖時返回的是 0,否則返回一個錯誤
pthread_mutex_trylock(& mutex_t)

五、NSLock(互斥鎖、對象鎖)

  • 0.283196s
// 初始化
NSLock *_lock = [[NSLock alloc]init];
// 加鎖
[_lock lock];
// 解鎖
[_lock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_lock tryLock];

六、NSCondition(條件鎖、對象鎖)

  • 0.293046s
// 初始化
NSCondition *_condition= [[NSCondition alloc]init];
// 加鎖
[_condition lock];
// 解鎖
[_condition unlock];
/*
其他功能接口
wait 進(jìn)入等待狀態(tài)
waitUntilDate:讓一個線程等待一定的時間
signal 喚醒一個等待的線程
broadcast 喚醒所有等待的線程
注: 所測時間波動太大, 有時候會快于 NSLock, 我取得中間值.
*/

七、NSConditionLock(條件鎖、對象鎖)

  • 0.950285s
// 初始化
NSConditionLock *_conditionLock = [[NSConditionLock alloc]init];
// 加鎖
[_conditionLock lock];
// 解鎖
[_conditionLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_conditionLock tryLock];
/*
其他功能接口
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER; //初始化傳入條件
- (void)lockWhenCondition:(NSInteger)condition;//條件成立觸發(fā)鎖
- (BOOL)tryLockWhenCondition:(NSInteger)condition;//嘗試條件成立觸發(fā)鎖
- (void)unlockWithCondition:(NSInteger)condition;//條件成立解鎖
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//觸發(fā)鎖 條件成立 并且在等待時間之內(nèi)
*/

八、NSRecursiveLock(遞歸鎖、對象鎖)

  • 0.473536s
// 初始化
NSRecursiveLock *_recursiveLock = [[NSRecursiveLock alloc]init];
// 加鎖
[_recursiveLock lock];
// 解鎖
[_recursiveLock unlock];
// 嘗試加鎖,可以加鎖則立即加鎖并返回 YES,反之返回 NO
[_recursiveLock tryLock];
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
- (BOOL)lockBeforeDate:(NSDate *)limit;//觸發(fā)鎖 在等待時間之內(nèi)
*/

九、@synchronized()遞歸鎖

  • 1.101924s
// 初始化
@synchronized(鎖對象){
}
底層封裝的pthread_mutex的PTHREAD_MUTEX_RECURSIVE 模式,
鎖對象來表示是否為同一把鎖

更多關(guān)于@synchronized;

十、pthread_mutex(recursive)(遞歸鎖)

  • 0.372398s
// 初始化
pthread_mutex_t mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); //初始化attr并且給它賦予默認(rèn)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //設(shè)置鎖類型,這邊是設(shè)置為遞歸鎖
pthread_mutex_init(&mutex_t, &attr);
pthread_mutexattr_destroy(&attr); //銷毀一個屬性對象,在重新進(jìn)行初始化之前該結(jié)構(gòu)不能重新使用
// 加鎖
pthread_mutex_lock(&mutex_t);
// 解鎖
pthread_mutex_unlock(&mutex_t);
/*
注: 遞歸鎖可以被同一線程多次請求,而不會引起死鎖。
即在同一線程中在未解鎖之前還可以上鎖, 執(zhí)行鎖中的代碼。
這主要是用在循環(huán)或遞歸操作中。
*/

性能總結(jié)

OSSpinLock                          0.097348s
dispatch_semaphore                  0.155043s
os_unfair_lock                      0.171789s
pthread_mutex                       0.262592s
NSLock                              0.283196s
pthread_mutex(recursive)            0.372398s
NSRecursiveLock                     0.473536s
NSConditionLock                     0.950285s
@synchronized                       1.101924s
注:建議正常鎖功能用 pthread_mutex ,os_unfair_lock (適配低版本)

鎖的注解

1、自旋鎖

OSSpinLock 就是典型的自旋鎖
自旋鎖的特點(diǎn)是在沒有獲取到鎖時既鎖已經(jīng)被添加,還沒有被解開時. 
OSSpinLock處于忙等狀態(tài),一直占用CPU資源,類似如下偽代碼:
while(鎖沒解開);

關(guān)于優(yōu)先級反轉(zhuǎn)問題
由于線程調(diào)度,每條線程的分配時間權(quán)重不一樣,當(dāng)權(quán)重小的線程先進(jìn)入OSSpinLock優(yōu)先加鎖,
當(dāng)權(quán)重大的線程再來訪問,就阻塞在這,可能權(quán)重大的線程會一直分配到cpu所以一直會進(jìn)來,
但是因?yàn)橛墟i,只能等待,權(quán)重小的線程得不到cpu資源分配,所以不會解鎖,造成一定程度的死鎖.

2、互斥鎖

os_unfair_lock 、pthread_mutex是典型的互斥鎖,在沒有獲取到鎖時既鎖已經(jīng)被添加,還沒有被解開時. 
它們都會讓當(dāng)前線程進(jìn)入休眠狀態(tài)既不占用CPU資源,但是為什么,互斥鎖比自旋鎖的效率低呢,
是因?yàn)樾菝?以及喚醒休眠,比忙等更加消耗CPU資源.

NSLock 封裝的pthread_mutex的PTHREAD_MUTEX_NORMAL 模式
NSRecursiveLock 封裝的pthread_mutex的PTHREAD_MUTEX_RECURSIVE 模式

3、條件鎖

在一定條件下,讓其等待休眠,并放開鎖,等接收到信號或者廣播,會從新喚起線程,并重新加鎖.
pthread_cond_wait(&_cond, &_mutex);
 // 信號
    pthread_cond_signal(&_cond);
 // 廣播
    pthread_cond_broadcast(&_cond);

像NSCondition封裝了pthread_mutex的以上幾個函數(shù)

NSConditionLock封裝了NSCondition

4、遞歸鎖

遞歸鎖的主要意思是,同一條線程可以加多把鎖.什么意思呢,就是相同的線程訪問一段代碼,
如果是加鎖的可以繼續(xù)加鎖,繼續(xù)往下走,不同線程來訪問這段代碼時,發(fā)現(xiàn)有鎖要等待所有鎖解開之后才可以繼續(xù)往下走.

NSRecursiveLock 封裝的pthread_mutex  的PTHREAD_MUTEX_RECURSIVE模式

作者:有毒的程序猿
鏈接:http://www.itdecent.cn/p/7e9dd2cb78a8
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 我覺得打游戲屏蔽臟話挺有必要的, 我走中單, 打野一直拿我藍(lán), 我開大打他路過交懲戒搶, 我氣的不行, 就罵...
    有毒的程序猿閱讀 7,480評論 4 38
  • 鎖是一種同步機(jī)制,用于多線程環(huán)境中對資源訪問的限制iOS中常見鎖的性能對比圖(摘自:ibireme): iOS鎖的...
    LiLS閱讀 1,626評論 0 6
  • 一、線程鎖相關(guān)概念 線程鎖:我們在使用多線程的時候多個線程可能會訪問同一塊資源,這樣就很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全...
    2525252472閱讀 483評論 0 2
  • 前言 在多線程開發(fā)中,常會遇到多個線程訪問修改數(shù)據(jù)。為了防止數(shù)據(jù)不一致或數(shù)據(jù)污染,通常采用加鎖機(jī)制來保證線程安全。...
    趙夢楠閱讀 1,151評論 0 5
  • 它靜默在身體里 一旦被驚動 便迅速繁殖 生長 阻斷一切感官 它的名字叫懷疑 直到將人毀滅 它永遠(yuǎn)不死 繼續(xù)蟄伏 尋...
    偏偏喜歡你sky閱讀 370評論 0 4

友情鏈接更多精彩內(nèi)容