在查看SDWebImage這種第三方的時(shí)候會(huì)發(fā)現(xiàn)有些地方有用到鎖,其中最常見(jiàn)的就是@ synchronized滓鸠,所以今天我們就由點(diǎn)到面的來(lái)了解一下iOS的各種鎖。下面我們用腦圖來(lái)分析下鎖的各種類(lèi)型:
問(wèn)題場(chǎng)景:
開(kāi)發(fā)數(shù)中會(huì)難免會(huì)遇到多線程競(jìng)爭(zhēng)資源的問(wèn)題婚度,從而帶來(lái)了線程安全的問(wèn)題伟葫。
所謂線程安全:當(dāng)一個(gè)線程訪問(wèn)數(shù)據(jù)的時(shí)候,其他線程不能對(duì)其訪問(wèn)部逮,直至該線程訪問(wèn)完畢娜汁。簡(jiǎn)單來(lái)講就是同一時(shí)刻對(duì)同一個(gè)數(shù)據(jù)操作的線程只有一個(gè)。只有確保了這樣甥啄,才能使數(shù)據(jù)不會(huì)被其他線程影響存炮。
那么,我們?cè)鯓颖WC線程安全呢蜈漓?
此時(shí),鎖就派上用場(chǎng)了宫盔,可以確保同一時(shí)刻只有同一個(gè)線程對(duì)同一個(gè)數(shù)據(jù)源進(jìn)行訪問(wèn)融虽。
1、@synchronized鎖
@synchronized(美 [,s?nkr?na?'ze??n]:同步)是OC層面的鎖灼芭,synchronized block 與 [_lock lock] & [_lock unlock] 效果相同有额,但語(yǔ)法更加簡(jiǎn)潔可讀,但代價(jià)是性能的降低彼绷。
官網(wǎng)介紹:防止不同的線程同時(shí)獲取相同的鎖巍佑。
知識(shí)網(wǎng)址:http://yulingtianxia.com/blog/2015/11/01/More-than-you-want-to-know-about-synchronized/
重點(diǎn):@synchronized 結(jié)構(gòu)在工作時(shí)為傳入的對(duì)象分配了一個(gè)遞歸鎖。所謂遞歸鎖是在被同一個(gè)線程重復(fù)調(diào)用時(shí)不會(huì)產(chǎn)生死鎖寄悯。NSRecursiveLock(遞歸鎖)類(lèi)也是這樣的萤衰,我們后面會(huì)有分析。
特殊情況:
1猜旬、你調(diào)用 sychronized 的每個(gè)對(duì)象脆栋,Objective-C runtime 都會(huì)為其分配一個(gè)遞歸鎖并存儲(chǔ)在哈希表中。
2洒擦、如果在 sychronized 內(nèi)部對(duì)象被釋放或被設(shè)為 nil 看起來(lái)都 OK椿争。不過(guò)這沒(méi)在文檔中說(shuō)明,所以我不會(huì)再生產(chǎn)代碼中依賴這條熟嫩。
3秦踪、注意不要向你的 sychronized block 傳入 nil!這將會(huì)從代碼中移走線程安全。你可以通過(guò)在 objc_sync_nil 上加斷點(diǎn)來(lái)查看是否發(fā)生了這樣的事情椅邓。
代碼如下:
- (void)synchronizedLock{
/*
NSMutableArray *_elements;
_elements在任何情況下都只會(huì)在一個(gè)線程中運(yùn)行
*/
@synchronized(_elements){
[_elements addObject:@"1"];
};
}
2舍扰、NSLock鎖
原理:NSLock實(shí)現(xiàn)了最基本的互斥鎖,遵循NSLocking協(xié)議希坚,通過(guò)lock和unLock來(lái)進(jìn)行鎖定于解鎖边苹。當(dāng)一個(gè)線程訪問(wèn)的時(shí)候,該線程獲得鎖裁僧,其他線程訪問(wèn)的時(shí)候个束,將被操作系統(tǒng)掛起,直到該線程釋放鎖聊疲,其他線程才能對(duì)其進(jìn)行訪問(wèn)茬底,從而確保線程安全。如果連續(xù)鎖定获洲,則會(huì)造成死鎖問(wèn)題阱表。
代碼如下:
/*
A :lock的最簡(jiǎn)單使用
*/
- (void)initLock{
//1、對(duì)鎖進(jìn)行初始化
_elements = [NSMutableArray array];
_lock = [[NSLock alloc] init];
}
- (void)push:(id)element{
//2贡珊、上鎖
[_lock lock];
[_elements addObject:element];
//3最爬、解鎖
[_lock unlock];
}
/*
B :lock的結(jié)合GCD多線程調(diào)用使用
*/
- (void)GDCAndLock{
_lock = [[NSLock alloc]init];
//在多個(gè)線程中調(diào)用。由于使用鎖的線程鎖是沒(méi)有執(zhí)行完畢的门岔,所以其他顯線程不能調(diào)用爱致,直到執(zhí)行完畢后,才允許其他線程調(diào)用寒随。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1");
[self lockFounction:[NSThread currentThread] num: 1];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
[self lockFounction:[NSThread currentThread] num: 2];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"3");
[self lockFounction:[NSThread currentThread] num: 3];
});
}
- (void)lockFounction:(NSThread *)thread num:(NSInteger) num {
[_lock lock];
NSLog(@"thread - %@, num - %ld", thread, num);
sleep(5);
[_lock unlock];
}
打印如圖:
/*
C :lock的tryLock和lockBeforeDate兩個(gè)方法的使用糠悯。
tryLock方法會(huì)嘗試加鎖,如果鎖不可用(已經(jīng)被鎖住)妻往,則并不會(huì)阻塞線程互艾,并返回NO。lockBeforeDate:方法會(huì)在所指定Date之前嘗試加鎖讯泣,如果在指定時(shí)間之前都不能加鎖纫普,則返回NO。
*/
- (void)tryLockAndDate{
_lock = [[NSLock alloc]init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//lockBeforeDate會(huì)在指定的時(shí)間之前加鎖判帮,所以已經(jīng)使用過(guò)[_lock lock]了.下面相當(dāng)于在當(dāng)前時(shí)間之前上鎖了局嘁。
[_lock lockBeforeDate:[NSDate date]];
NSLog(@"1需要線程同步的操作1 開(kāi)始");
sleep(2);
NSLog(@"1需要線程同步的操作1 結(jié)束");
[_lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
if ([_lock tryLock]) {//嘗試獲取鎖,如果獲取不到返回NO晦墙,不會(huì)阻塞該線程
NSLog(@"2鎖可用的操作");
[_lock unlock];
}else{
NSLog(@"2鎖不可用的操作");
}
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];
if ([_lock lockBeforeDate:date]) {
//嘗試在未來(lái)的3s內(nèi)獲取鎖悦昵,并阻塞該線程,如果3s內(nèi)獲取不到恢復(fù)線程, 返回NO,不會(huì)阻塞該線程
NSLog(@"2沒(méi)有超時(shí)晌畅,獲得鎖");
[_lock unlock];
}else{
NSLog(@"2超時(shí)但指,沒(méi)有獲得鎖");
}
});
}
3、遞歸鎖NSRecursiveLock
NSRecursiveLock遞歸鎖可以被同一線程多次請(qǐng)求,但不會(huì)引起死鎖棋凳。這主要是用在循環(huán)或者遞歸操作場(chǎng)景中拦坠。
- (void)useNSRecursiveLock{
//如果使用_lock會(huì)招致死鎖,因?yàn)楸煌粋€(gè)線程多次調(diào)用剩岳。每次進(jìn)入這個(gè)block時(shí)贞滨,都會(huì)去加一次鎖,而從第二次開(kāi)始拍棕,由于鎖已經(jīng)被使用了且沒(méi)有解鎖晓铆,所以它需要等待鎖被解除,這樣就導(dǎo)致了死鎖绰播,線程被阻塞住了骄噪。
// _lock = [[NSLock alloc] init];
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//創(chuàng)建一個(gè)靜態(tài)方法,block方法
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value = %d", value);
sleep(1);
RecursiveMethod(value - 1);
}
[lock unlock];
};
RecursiveMethod(5);//方法內(nèi)部判斷來(lái)執(zhí)行5次
});
}
4蠢箩、NSConditionLock條件鎖
當(dāng)我們?cè)谑褂枚嗑€程的時(shí)候链蕊,只有一把會(huì)lock和unlock的鎖就不能滿足我們的需要了。因?yàn)槠胀ǖ逆i只關(guān)心鎖與不鎖谬泌,但是并不在乎什么時(shí)候才能開(kāi)鎖滔韵,而在處理資源共享場(chǎng)景的時(shí)候,多數(shù)情況下只有滿足一定條件下才能打開(kāi)這把鎖呵萨。(Condition:美 [k?n'd???n] 條件)
NSConditionLock實(shí)現(xiàn)步驟:
NSConditionLock實(shí)現(xiàn)了NSLocking協(xié)議奏属,一個(gè)線程會(huì)等待另一個(gè)線程unlock或者unlockWithCondition:之后再走lock或者lockWhenCondition:之后的代碼。
鎖定和解鎖的調(diào)用可以隨意組合潮峦,也就是說(shuō) lock、lockWhenCondition:與unlock勇婴、unlockWithCondition: 是可以按照自己的需求隨意組合的忱嘹。
劃重點(diǎn):
1、只有 condition 參數(shù)與初始化時(shí)候的 condition 相等耕渴,lock 才能正確進(jìn)行加鎖操作拘悦。
2、unlockWithCondition: 并不是當(dāng) condition 符合條件時(shí)才解鎖橱脸,而是解鎖之后础米,修改 condition 的值。
/*
在線程 1 解鎖成功之后添诉,線程 2 并沒(méi)有加鎖成功屁桑,而是繼續(xù)等了 1 秒之后線程 3 加鎖成功,這是因?yàn)榫€程 2 的加鎖條件不滿足栏赴,初始化時(shí)候的 condition 參數(shù)為 0蘑斧,而線程 2
加鎖條件是 condition 為 1,所以線程 2 加鎖失敗。
lockWhenCondition 與 lock 方法類(lèi)似竖瘾,加鎖失敗會(huì)阻塞線程沟突,所以線程 2 會(huì)被阻塞著。
tryLockWhenCondition: 方法就算條件不滿足捕传,也會(huì)返回 NO惠拭,不會(huì)阻塞當(dāng)前線程。
lockWhenCondition:beforeDate:方法會(huì)在約定的時(shí)間內(nèi)一直等待 condition 變?yōu)?2庸论,并阻塞當(dāng)前線程职辅,直到超時(shí)后返回 NO。
*/
代碼:
- (void)nsconditionlock {
NSConditionLock * cjlock = [[NSConditionLock alloc] initWithCondition:0];
//1葡公、線程 1 解鎖成功
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cjlock lock];
NSLog(@"線程1加鎖成功");
sleep(1);//線程休眠一秒
[cjlock unlock];
NSLog(@"線程1解鎖成功");
});
//2罐农、初始化時(shí)候的 condition 參數(shù)為0,所以此處加鎖失敗催什,返回NO涵亏,此處線程阻塞。全部現(xiàn)成執(zhí)行完畢后執(zhí)行此處鎖
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);//線程休眠一秒
[cjlock lockWhenCondition:1];
NSLog(@"線程2加鎖成功");
[cjlock unlock];
NSLog(@"線程2解鎖成功");
});
//3蒲凶、tryLockWhenCondition嘗試加鎖 初始化時(shí)候的 condition 參數(shù)為0气筋,所以此處加鎖成功。方法就算條件不滿足旋圆,也會(huì)返回 NO宠默,不會(huì)阻塞當(dāng)前線程。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
if ([cjlock tryLockWhenCondition:0]) {
NSLog(@"線程3加鎖成功");
sleep(2);
/*
A:成功案例
這里會(huì)先解鎖當(dāng)前的鎖灵巧,之后修改condition的值為100.在下一個(gè)condition為100的線程中會(huì)加解鎖成功搀矫,如果下個(gè)鎖中的condition等待的值不是100,那么就會(huì)導(dǎo)致加鎖失敗刻肄。
*/
[cjlock unlockWithCondition:100];
NSLog(@"線程3解鎖成功");
/*
B:失敗案例
[cjlock unlockWithCondition:4];
NSLog(@"線程3仍然會(huì)解鎖成功瓤球,之后修改condition的值為4");
*/
} else {
NSLog(@"線程3嘗試加鎖失敗");
}
});
//4、lockWhenCondition:beforeDate:方法會(huì)在約定的時(shí)間內(nèi)一直等待 condition 變?yōu)?2敏弃,并阻塞當(dāng)前線程卦羡,直到超時(shí)后返回 NO。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cjlock lockWhenCondition:100 beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"線程100加鎖成功");
[cjlock unlockWithCondition:1];
NSLog(@"線程100解鎖成功");
} else {
NSLog(@"線程100嘗試加鎖失敗");
}
});
}
5麦到、NSCondition
定義及使用:NSCondition 是一種特殊類(lèi)型的鎖绿饵,通過(guò)它可以實(shí)現(xiàn)不同線程的調(diào)度。A線程被某一個(gè)條件所阻塞瓶颠,直到B線程滿足該條件拟赊,從而發(fā)送信號(hào)給A線程使得A線程繼續(xù)執(zhí)行,例如:你可以開(kāi)啟一個(gè)線程下載圖片步清,一個(gè)線程處理圖片要门。這樣的話虏肾,需要處理圖片的線程由于沒(méi)有圖片會(huì)阻塞,當(dāng)下載線程下載完成之后欢搜,則滿足了需要處理圖片的線程的需求封豪,這樣可以給定一個(gè)信號(hào),讓處理圖片的線程恢復(fù)運(yùn)行炒瘟。
重點(diǎn):
1吹埠、NSCondition 的對(duì)象實(shí)際上作為一個(gè)鎖和一個(gè)線程檢查器,鎖上之后疮装,其他線程也能繼續(xù)上鎖缘琅,之后根據(jù)條件決定是否繼續(xù)運(yùn)行線程,如果線程進(jìn)入waiting狀態(tài)廓推,當(dāng)其他線程中的該鎖執(zhí)行signal(信號(hào))或者broadcast(廣播)時(shí)刷袍,線程被喚醒,繼續(xù)運(yùn)行該線程之后的方法樊展。呻纹。
2、NSCondition 可以手動(dòng)控制現(xiàn)成的掛起和喚醒专缠,可以利用這個(gè)特性設(shè)置依賴雷酪。
特別提醒:
signal只是喚醒單個(gè)線程,broadcast喚醒所有的線程涝婉。
broadcast :廣播 { 英 ['br??dkɑ?st] 美 ['br?dk?st]}
- (void)nsCondition {
NSCondition * cjcondition = [NSCondition new];
/*
在加上鎖之后哥力,調(diào)用條件對(duì)象的 wait 或 waitUntilDate: 方法來(lái)阻塞線程,直到條件對(duì)象發(fā)出喚醒信號(hào)或者超時(shí)之后墩弯,再進(jìn)行之后的操作吩跋。
*/
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[cjcondition lock];
NSLog(@"線程1線程加鎖----NSTreat:%@",[NSThread currentThread]);
[cjcondition wait];
NSLog(@"線程1線程喚醒");
[cjcondition unlock];
NSLog(@"線程1線程解鎖");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[cjcondition lock];
NSLog(@"線程2線程加鎖----NSTreat:%@",[NSThread currentThread]);
if ([cjcondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"線程2線程喚醒");
[cjcondition unlock];
NSLog(@"線程2線程解鎖");
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
/*
1、休眠時(shí)間如果超過(guò)了線程中條件鎖等待的時(shí)間渔工,那么所有的線程都不會(huì)被喚醒钞澳。不管是哪一個(gè)線程中設(shè)置的時(shí)間,都不能超時(shí)涨缚,否則就會(huì)返回NO,全部不執(zhí)行策治!切記切記脓魏!
2、一次只能喚醒一個(gè)線程通惫,要調(diào)用多次才可以喚醒多個(gè)線程茂翔,如下調(diào)用兩次,將休眠的兩個(gè)線程解鎖履腋。
3珊燎、喚醒的順序?yàn)榫€程添加的順序惭嚣。
*/
[cjcondition signal];
[cjcondition signal];
//一次性全部喚醒
//[cjcondition broadcast];
});
}
6、dispatch_semaphore信號(hào)量
GCD的信號(hào)量機(jī)制實(shí)現(xiàn)鎖悔政,等待信號(hào)和發(fā)送信號(hào)晚吞。
1、dispatch_semaphore 是 GCD 用來(lái)同步的一種方式谋国,與他相關(guān)的只有三個(gè)函數(shù)槽地,一個(gè)是創(chuàng)建信號(hào)量,一個(gè)是等待信號(hào)芦瘾,一個(gè)是發(fā)送信號(hào)捌蚊。
2、dispatch_semaphore的機(jī)制就是當(dāng)有多個(gè)線程進(jìn)行訪問(wèn)的時(shí)候近弟,只要有一個(gè)獲得了信號(hào)缅糟,其他線程就必須等待該信號(hào)的釋放。
1祷愉、dispatch_semaphore 和 NSCondition 類(lèi)似窗宦,都是一種基于信號(hào)的同步方式,但 NSCondition 信號(hào)只能發(fā)送谣辞,不能保存(如果沒(méi)有線程在等待迫摔,則發(fā)送的信號(hào)會(huì)失效)。而 dispatch_semaphore 能保存發(fā)送的信號(hào)泥从。dispatch_semaphore 的核心是 dispatch_semaphore_t 類(lèi)型的信號(hào)量句占。
2、dispatch_semaphore_create(1) 方法可以創(chuàng)建一個(gè) dispatch_semaphore_t ( 英 ['sem?f??])類(lèi)型的信號(hào)量躯嫉,設(shè)定信號(hào)量的初始值為 1纱烘。注意,這里的傳入的參數(shù)必須大于或等于 0,否則 dispatch_semaphore_create 會(huì)返回 NULL皆看。
3遗锣、dispatch_semaphore_wait(semaphore, overTime); 方法會(huì)判斷 semaphore 的信號(hào)值是否大于 0。大于 0 不會(huì)阻塞線程哺壶,消耗掉一個(gè)信號(hào),執(zhí)行后續(xù)任務(wù)蜒谤。如果信號(hào)值為 0山宾,該線程會(huì)和 NSCondition 一樣直接進(jìn)入 waiting狀態(tài),等待其他線程發(fā)送信號(hào)喚醒該線程執(zhí)行后續(xù)任務(wù)鳍徽,或者當(dāng) overTime 時(shí)限到了资锰,也會(huì)執(zhí)行后續(xù)任務(wù)。
4阶祭、dispatch_semaphore_signal(semaphore); 發(fā)送信號(hào)绷杜,如果沒(méi)有等待的線程接受信號(hào)直秆,則使 signal 信號(hào)值加一(做到對(duì)信號(hào)的保存)。
5鞭盟、一個(gè) dispatch_semaphore_wait(semaphore, overTime); 方法會(huì)去對(duì)應(yīng)一個(gè) dispatch_semaphore_signal(semaphore); 看起來(lái)像 NSLock 的 lock 和 unlock圾结,其實(shí)可以這樣理解,區(qū)別只在于有信號(hào)量這個(gè)參數(shù)懊缺,疫稿,lock unlock 只能同一時(shí)間,只能有一個(gè)線程訪問(wèn)被保護(hù)的臨界區(qū)鹃两,而如果信號(hào)量參數(shù)初始值為 x遗座,那么就會(huì)有 x 個(gè)線程可以同時(shí)訪問(wèn)被保護(hù)的臨界區(qū)。
*/
- (void)useDispatch_semaphore {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// overTime設(shè)置為6秒
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overTime);
NSLog(@"線程1開(kāi)始");
sleep(5);
NSLog(@"線程1結(jié)束");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, overTime);
NSLog(@"線程2開(kāi)始");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, overTime);
NSLog(@"線程3開(kāi)始");
dispatch_semaphore_signal(semaphore);
});
}
7俊扳、OSSpinLock自旋鎖
首先導(dǎo)入:#import <libkern/OSAtomic.h>
OSSpinLock 是一種自旋鎖途蒋,和互斥鎖類(lèi)似,都是為了保證線程安全的鎖馋记。但是這是不一樣的:
互斥鎖:當(dāng)一個(gè)線程獲得此鎖之后号坡,其他線程再獲取將會(huì)被阻塞,直到該鎖被釋放梯醒。
自旋鎖:當(dāng)一個(gè)線程獲得此鎖之后宽堆,其他線程將會(huì)一直循環(huán)查看該鎖是否被釋放。鎖比較適用于鎖的持有者保存時(shí)間較短的情況下茸习。
OSSpinLock自旋鎖只有加鎖畜隶、嘗試加鎖和解鎖三個(gè)方法。
/*
YY大神 @ibireme 的文章也有說(shuō)這個(gè)自旋鎖存在優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題号胚,具體文章可以戳 不再安全的 OSSpinLock籽慢,而 OSSpinLock 在iOS 10.0中被 <os/lock.h> 中的 os_unfair_lock 取代。
常用的相關(guān)API:
// 初始化
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
// 加鎖
os_unfair_lock_lock(unfairLock);
// 嘗試加鎖
BOOL b = os_unfair_lock_trylock(unfairLock);
// 解鎖
os_unfair_lock_unlock(unfairLock);
os_unfair_lock 用法和 OSSpinLock 基本一致猫胁,就不一一列出了箱亿。
*/
- (void)osspinlock {
__block OSSpinLock theLock = OS_SPINLOCK_INIT;//在iOS10之后被ns_unfair_lock替換
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
NSLog(@"線程1開(kāi)始");
sleep(3);
NSLog(@"線程1結(jié)束");
OSSpinLockUnlock(&theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
sleep(1);
NSLog(@"線程2");
OSSpinLockUnlock(&theLock);
});
}
8、鎖的總結(jié)
其實(shí)每一種鎖基本上都是加鎖弃秆、等待届惋、解鎖的步驟,理解了這三個(gè)步驟就可以幫你快速的學(xué)會(huì)各種鎖的用法菠赚。
1盼樟、@synchronized 的效率最低,不過(guò)它的確用起來(lái)最方便锈至,所以如果沒(méi)什么性能瓶頸的話,可以選擇使用 @synchronized译秦。
2峡捡、當(dāng)性能要求較高時(shí)候击碗,可以使用 pthread_mutex 或者 dispath_semaphore,由于 OSSpinLock 不能很好的保證線程安全们拙,而在只有在 iOS10 中才有 os_unfair_lock 稍途,所以,前兩個(gè)是比較好的選擇砚婆。既可以保證速度械拍,又可以保證線程安全。
3装盯、對(duì)于 NSLock 及其子類(lèi)坷虑,速度來(lái)說(shuō) NSLock < NSCondition < NSRecursiveLock < NSConditionLock 。
好了埂奈,要趕火車(chē)回家過(guò)年了迄损,來(lái)年開(kāi)春了在接著寫(xiě)啦。大家新春快樂(lè)账磺!?
參考資料:
http://www.jb51.net/article/127573.htm
http://blog.csdn.net/liupinghui/article/details/67637830
最后奉上本人總結(jié)的代碼:https://github.com/caiqingchong/iOSLock