自旋鎖和互斥鎖
共同點:都能保證同一時刻只能有一個線程操作鎖住的代碼惑畴。都能保證線程安全蛋欣。
不同點:
- 互斥鎖(mutex):當(dāng)上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖住)如贷,那么下一個線程會進入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢(sleep-waiting)陷虎,當(dāng)上一個線程的任務(wù)執(zhí)行完畢,下一個線程會自動喚醒然后執(zhí)行任務(wù)倒得。
- 自旋鎖(Spin lock):當(dāng)上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖仔汉臁),那么下一個線程會一直等待(busy-waiting)霞掺,當(dāng)上一個線程的任務(wù)執(zhí)行完畢谊路,下一個線程會立即執(zhí)行。
- 由于自旋鎖不會引起調(diào)用者睡眠菩彬,所以自旋鎖的效率遠高于互斥鎖
- 自旋鎖會一直占用CPU缠劝,也可能會造成死鎖
原子操作
nonatomic:非原子屬性,非線程安全骗灶,適合小內(nèi)存移動設(shè)備
atomic:原子屬性惨恭,default,線程安全(內(nèi)部使用自旋鎖)耙旦,消耗大量資源脱羡,單寫多讀,只為setter加鎖,不影響getter方法
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
- @synchronized 關(guān)鍵字加鎖
@synchronized是iOS中最常見的鎖锉罐,也是性能最差的一種帆竹,具體用法如下:
- (void)synchronized {
NSObject * cjobj = [NSObject new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(cjobj){
NSLog(@"線程1開始");
sleep(3);
NSLog(@"線程1結(jié)束");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized(cjobj){
NSLog(@"線程2");
}
});
}
線程2會等待線程1執(zhí)行完以后才會執(zhí)行,這種鎖在使用時要確保該鎖的唯一標(biāo)識符是相同的才行脓规,即為同一個對象即可栽连,如上述代碼中的cjobj;
優(yōu)點:就是我們不需要在代碼中顯式的創(chuàng)建鎖對象侨舆,便可以實現(xiàn)鎖的機制秒紧,但作為一種預(yù)防措施,@synchronized 塊會隱式的添加一個異常處理例程來保護代碼挨下,該處理例程會在異常拋出的時候自動的釋放互斥鎖熔恢。
缺點:隱式的異常處理例程帶來額外的開銷,造成性能差臭笆。
2绩聘、NSLock(互斥鎖)
iOS中NSLock類的.h文件,從代碼中可以看出耗啦,該類分成了幾個子類:NSLock、NSConditionLock机杜、NSRecursiveLock帜讲、NSCondition,然后有一個 NSLocking 協(xié)議:
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
雖然 NSLock椒拗、NSConditionLock似将、NSRecursiveLock、NSCondition 都遵循的了 NSLocking 協(xié)議蚀苛,但是它們并不相同在验。
2.1 NSLock 對象鎖
NSLock實現(xiàn)了最基本的鎖,遵循NSLoaking協(xié)議堵未,通過lock和unlock來進行加鎖和解鎖
源碼內(nèi)容:
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
使用方法如下:
- (void)lockTest {
NSLock *lock = [[NSLock alloc]init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"線程1加鎖成功");
sleep(2);
[lock unlock];
NSLog(@"線程1解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"線程2加鎖成功");
sleep(2);
[lock unlock];
NSLog(@"線程2解鎖成功");
});
}
除 lock 和 unlock 方法外腋舌,NSLock 還提供了 tryLock 和 lockBeforeDate:兩個方法,這兩個方法我就不給大家演示了渗蟹,感興趣的可以自己演示下块饺,我把方法對應(yīng)的作用高速大家。
tryLock 并不會阻塞線程雌芽,[cjlock tryLock] 能加鎖返回 YES授艰,不能加鎖返回 NO,然后都會執(zhí)行后續(xù)代碼世落。
這里順便提一下 trylock 和 lock 使用場景:當(dāng)前線程鎖失敗淮腾,也可以繼續(xù)其它任務(wù),用 trylock 合適;當(dāng)前線程只有鎖成功后谷朝,才會做一些有意義的工作洲押,那就 lock,沒必要輪詢 trylock徘禁。以下的鎖都是這樣诅诱。
lockBeforeDate: 方法會在所指定 Date 之前嘗試加鎖,會阻塞線程送朱,如果在指定時間之前都不能加鎖娘荡,則返回 NO,指定時間之前能加鎖驶沼,則返回 YES炮沐。
由于是互斥鎖,當(dāng)一個線程進行訪問的時候回怜,該線程獲得鎖大年,其他線程進行訪問的時候,將被操作系統(tǒng)掛起玉雾,直到該線程釋放鎖翔试,其他線程才能對其進行訪問,從而卻確保了線程安全复旬。但是如果連續(xù)鎖定兩次垦缅,則會造成死鎖問題。
2.2驹碍、 NSRecursiveLock 遞歸鎖
NSRecursiveLock是遞歸鎖壁涎,可以被一個線程多次獲得,而不會引起死鎖志秃。它記錄了成功獲得鎖的次數(shù)怔球,每一次成功的獲得鎖,必須有一個配套的釋放鎖和其對應(yīng)浮还,這樣才不會引起死鎖竟坛。NSRecursiveLock 會記錄上鎖和解鎖的次數(shù)温眉,當(dāng)二者平衡的時候管搪,才會釋放鎖,其它線程才可以上鎖成功左驾。
源碼內(nèi)容:
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
使用方法如下:
- (void)nsrecursivelock{
NSRecursiveLock * cjlock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
[cjlock lock];
NSLog(@"%d加鎖成功",value);
if (value > 0) {
NSLog(@"value:%d", value);
RecursiveBlock(value - 1);
}
[cjlock unlock];
NSLog(@"%d解鎖成功",value);
};
RecursiveBlock(3);
});
}
2019-03-10 16:42:51.487615+0800 AllSuoUser[45654:1840908] 3加鎖成功
2019-03-10 16:42:51.487773+0800 AllSuoUser[45654:1840908] value:3
2019-03-10 16:42:51.487866+0800 AllSuoUser[45654:1840908] 2加鎖成功
2019-03-10 16:42:51.487963+0800 AllSuoUser[45654:1840908] value:2
2019-03-10 16:42:51.488056+0800 AllSuoUser[45654:1840908] 1加鎖成功
2019-03-10 16:42:51.488157+0800 AllSuoUser[45654:1840908] value:1
2019-03-10 16:42:51.488256+0800 AllSuoUser[45654:1840908] 0加鎖成功
2019-03-10 16:42:51.488350+0800 AllSuoUser[45654:1840908] 0解鎖成功
2019-03-10 16:42:51.488468+0800 AllSuoUser[45654:1840908] 1解鎖成功
2019-03-10 16:42:51.488577+0800 AllSuoUser[45654:1840908] 2解鎖成功
2019-03-10 16:42:51.488681+0800 AllSuoUser[45654:1840908] 3解鎖成功
由以上內(nèi)容總結(jié):
如果用 NSLock 的話延刘,cjlock 先鎖上了漫试,但未執(zhí)行解鎖的時候,就會進入遞歸的下一層碘赖,而再次請求上鎖驾荣,阻塞了該線程外构,線程被阻塞了,自然后面的解鎖代碼不會執(zhí)行播掷,而形成了死鎖审编。而 NSRecursiveLock 遞歸鎖就是為了解決這個問題。
2.3歧匈、NSConditionLock
NSConditionLock 對象所定義的互斥鎖可以在使得在某個條件下進行鎖定和解鎖垒酬,它和 NSLock 類似,都遵循 NSLocking 協(xié)議件炉,方法都類似勘究,只是多了一個 condition 屬性,以及每個操作都多了一個關(guān)于 condition 屬性的方法斟冕,例如 tryLock口糕、tryLockWhenCondition:,所以 NSConditionLock 可以稱為條件鎖磕蛇。
- 只有 condition 參數(shù)與初始化時候的 condition 相等景描,lock 才能正確進行加鎖操作。
- unlockWithCondition: 并不是當(dāng) condition 符合條件時才解鎖秀撇,而是解鎖之后超棺,修改 condition 的值。
源碼內(nèi)容:
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (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;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
使用方法介紹:
- (void)nsconditionlock {
NSConditionLock * cjlock = [[NSConditionLock alloc] initWithCondition:0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cjlock lock];
NSLog(@"線程1加鎖成功");
sleep(1);
[cjlock unlock];
NSLog(@"線程1解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
[cjlock lockWhenCondition:1];
NSLog(@"線程2加鎖成功");
[cjlock unlock];
NSLog(@"線程2解鎖成功");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
if ([cjlock tryLockWhenCondition:0]) {
NSLog(@"線程3加鎖成功");
sleep(2);
[cjlock unlockWithCondition:2];
NSLog(@"線程3解鎖成功");
} else {
NSLog(@"線程3嘗試加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cjlock lockWhenCondition:2 beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"線程4加鎖成功");
[cjlock unlockWithCondition:1];
NSLog(@"線程4解鎖成功");
} else {
NSLog(@"線程4嘗試加鎖失敗");
}
});
}
2017-10-19 15:09:44.010992+0800 Thread-Lock[39230:853946] 線程1加鎖成功
2017-10-19 15:09:45.012045+0800 Thread-Lock[39230:853946] 線程1解鎖成功
2017-10-19 15:09:46.012692+0800 Thread-Lock[39230:853947] 線程3加鎖成功
2017-10-19 15:09:48.016536+0800 Thread-Lock[39230:853947] 線程3解鎖成功
2017-10-19 15:09:48.016564+0800 Thread-Lock[39230:853944] 線程4加鎖成功
2017-10-19 15:09:48.017039+0800 Thread-Lock[39230:853944] 線程4解鎖成功
2017-10-19 15:09:48.017040+0800 Thread-Lock[39230:853945] 線程2加鎖成功
2017-10-19 15:09:48.017215+0800 Thread-Lock[39230:853945] 線程2解鎖成功
由以上內(nèi)容總結(jié):
- 在線程 1 解鎖成功之后呵燕,線程 2 并沒有加鎖成功说搅,而是繼續(xù)等了 1 秒之后線程 3 加鎖成功,這是因為線程 2 的加鎖條件不滿足虏等,初始化時候的 condition 參數(shù)為 0,而線程 2
- 加鎖條件是 condition 為 1适肠,所以線程 2 加鎖失敗霍衫。
- lockWhenCondition 與 lock 方法類似,加鎖失敗會阻塞線程侯养,所以線程 2 會被阻塞著敦跌。
- tryLockWhenCondition: 方法就算條件不滿足,也會返回 NO逛揩,不會阻塞當(dāng)前線程柠傍。
- lockWhenCondition:beforeDate:方法會在約定的時間內(nèi)一直等待 condition 變?yōu)?2,并阻塞當(dāng)前線程辩稽,直到超時后返回 NO惧笛。
- 鎖定和解鎖的調(diào)用可以隨意組合,也就是說 lock逞泄、lockWhenCondition:與unlock患整、unlockWithCondition: 是可以按照自己的需求隨意組合的拜效。
2.4、 NSCondition
NSCondition 是一種特殊類型的鎖各谚,通過它可以實現(xiàn)不同線程的調(diào)度紧憾。一個線程被某一個條件所阻塞,直到另一個線程滿足該條件從而發(fā)送信號給該線程使得該線程可以正確的執(zhí)行昌渤。比如說赴穗,你可以開啟一個線程下載圖片,一個線程處理圖片膀息。這樣的話般眉,需要處理圖片的線程由于沒有圖片會阻塞,當(dāng)下載線程下載完成之后履婉,則滿足了需要處理圖片的線程的需求煤篙,這樣可以給定一個信號,讓處理圖片的線程恢復(fù)運行毁腿。
- NSCondition 的對象實際上作為一個鎖和一個線程檢查器辑奈,鎖上之后其它線程也能上鎖,而之后可以根據(jù)條件決定是否繼續(xù)運行線程已烤,即線程是否要進入 waiting 狀態(tài)鸠窗,如果進入 waiting 狀態(tài),當(dāng)其它線程中的該鎖執(zhí)行 signal 或者 broadcast 方法時胯究,線程被喚醒稍计,繼續(xù)運行之后的方法。
- NSCondition 可以手動控制線程的掛起與喚醒裕循,可以利用這個特性設(shè)置依賴臣嚣。
源碼解析:
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait; //掛起線程
- (BOOL)waitUntilDate:(NSDate *)limit; //什么時候掛起線程
- (void)signal; // 喚醒一條掛起線程
- (void)broadcast; //喚醒所有掛起線程
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
方法使用:
- (void)nscondition {
NSCondition * cjcondition = [NSCondition new];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[cjcondition lock];
NSLog(@"線程1線程加鎖");
[cjcondition wait];
NSLog(@"線程1線程喚醒");
[cjcondition unlock];
NSLog(@"線程1線程解鎖");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[cjcondition lock];
NSLog(@"線程2線程加鎖");
if ([cjcondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {
NSLog(@"線程2線程喚醒");
[cjcondition unlock];
NSLog(@"線程2線程解鎖");
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
[cjcondition signal];//喚起第一條開啟的線程
[cjcondition broadcast];//喚起所有線程
});
}
2017-10-19 17:15:48.410316+0800 Thread-Lock[40011:943638] 線程1線程加鎖
2017-10-19 17:15:48.410757+0800 Thread-Lock[40011:943640] 線程2線程加鎖
2017-10-19 17:15:50.414288+0800 Thread-Lock[40011:943638] 線程1線程喚醒
2017-10-19 17:15:50.414454+0800 Thread-Lock[40011:943638] 線程1線程解鎖
由以上內(nèi)容總結(jié):
1、在加上鎖之后剥哑,調(diào)用條件對象的 wait 或 waitUntilDate: 方法來阻塞線程硅则,直到條件對象發(fā)出喚醒信號或者超時之后,再進行之后的操作株婴。
2怎虫、signal 和 broadcast 方法的區(qū)別在于,signal 只是一個信號量困介,只能喚醒一個等待的線程大审,想喚醒多個就得多次調(diào)用,而 broadcast 可以喚醒所有在等待的線程座哩。
3徒扶、 dispatch_semaphore 信號量實現(xiàn)加鎖(GCD)
dispatch_semaphore 使用信號量機制實現(xiàn)鎖,等待信號和發(fā)送信號根穷。
- dispatch_semaphore 是 GCD 用來同步的一種方式酷愧,與他相關(guān)的只有三個函數(shù)驾诈,一個是創(chuàng)建信號量,一個是等待信號溶浴,一個是發(fā)送信號乍迄。
- dispatch_semaphore 的機制就是當(dāng)有多個線程進行訪問的時候,只要有一個獲得了信號士败,其他線程的就必須等待該信號釋放闯两。
相關(guān)的API:
dispatch_semaphore_create(long value);
dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull dsema, dispatch_time_t timeout);
dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull dsema);
用法如下:
- (void)dispatch_semaphore {
//創(chuàng)建信號量,必須大于1谅将,如果是N漾狼,表示同時可有N條線程執(zhí)行
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//給信號量設(shè)置最大等待時間,超過這個時間則不用等待
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, overTime);
NSLog(@"線程1開始");
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開始");
dispatch_semaphore_signal(semaphore);
});
}
2017-10-19 18:30:37.672490+0800 Thread-Lock[40569:993613] 線程1開始
2017-10-19 18:30:42.673845+0800 Thread-Lock[40569:993613] 線程1結(jié)束
2017-10-19 18:30:42.674165+0800 Thread-Lock[40569:993612] 線程2開始
以上總結(jié):
- dispatch_semaphore 和 NSCondition 類似饥臂,都是一種基于信號的同步方式逊躁,但 NSCondition 信號只能發(fā)送,不能保存(如果沒有線程在等待隅熙,則發(fā)送的信號會失效)稽煤。而 dispatch_semaphore 能保存發(fā)送的信號。dispatch_semaphore 的核心是 dispatch_semaphore_t 類型的信號量囚戚。
- dispatch_semaphore_create(1) 方法可以創(chuàng)建一個 dispatch_semaphore_t 類型的信號量酵熙,設(shè)定信號量的初始值為 1。注意驰坊,這里的傳入的參數(shù)必須大于或等于 0匾二,否則 dispatch_semaphore_create 會返回 NULL。
- dispatch_semaphore_wait(semaphore, overTime); 方法會判斷 semaphore 的信號值是否大于 0拳芙。大于 0 不會阻塞線程察藐,消耗掉一個信號,執(zhí)行后續(xù)任務(wù)舟扎。如果信號值為 0转培,該線程會和 NSCondition 一樣直接進入 waiting 狀態(tài),等待其他線程發(fā)送信號喚醒線程去執(zhí)行后續(xù)任務(wù)浆竭,或者當(dāng) overTime 時限到了,也會執(zhí)行后續(xù)任務(wù)惨寿。
- dispatch_semaphore_signal(semaphore); 發(fā)送信號邦泄,如果沒有等待的線程接受信號,則使 signal 信號值加一(做到對信號的保存)裂垦。
- 一個 dispatch_semaphore_wait(semaphore, overTime); 方法會去對應(yīng)一個 dispatch_semaphore_signal(semaphore); 看起來像 NSLock 的 lock 和 unlock顺囊,其實可以這樣理解,區(qū)別只在于有信號量這個參數(shù)蕉拢,lock unlock 只能同一時間特碳,一個線程訪問被保護的臨界區(qū)诚亚,而如果 dispatch_semaphore 的信號量初始值為 x ,則可以有 x 個線程同時訪問被保護的臨界區(qū)午乓。
4站宗、pthread_mutex與 pthread_mutex(recursive) 互斥鎖(C語言)
pthread 表示 POSIX thread,定義了一組跨平臺的線程相關(guān)的 API益愈,POSIX 互斥鎖是一種超級易用的互斥鎖梢灭,使用的時候:
- 只需要使用 pthread_mutex_init 初始化一個 pthread_mutex_t,
- pthread_mutex_lock 或者 pthread_mutex_trylock 來鎖定 蒸其,
- pthread_mutex_unlock 來解鎖敏释,
- 當(dāng)使用完成后,記得調(diào)用 pthread_mutex_destroy 來銷毀鎖摸袁。
常用的相關(guān)的API:
//初始化
pthread_mutex_init(pthread_mutex_t *restrict _Nonnull, const pthread_mutexattr_t *restrict _Nullable);
//加鎖
pthread_mutex_lock(pthread_mutex_t * _Nonnull);
//嘗試是鎖是否可用
pthread_mutex_trylock(pthread_mutex_t * _Nonnull);
//解鎖
pthread_mutex_unlock(pthread_mutex_t * _Nonnull);
用法:
- (void)pthread_mutex {
__block pthread_mutex_t cjlock;
pthread_mutex_init(&cjlock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&cjlock);
NSLog(@"線程1開始");
sleep(3);
NSLog(@"線程1結(jié)束");
pthread_mutex_unlock(&cjlock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&cjlock);
NSLog(@"線程2");
pthread_mutex_unlock(&cjlock);
});
}
5钥顽、OSSpinLock (暫不建議使用)
OSSpinLock 是一種自旋鎖,和互斥鎖類似靠汁,都是為了保證線程安全的鎖蜂大。但二者的區(qū)別是不一樣的,對于互斥鎖膀曾,當(dāng)一個線程獲得這個鎖之后县爬,其他想要獲得此鎖的線程將會被阻塞,直到該鎖被釋放添谊。但自選鎖不一樣财喳,當(dāng)一個線程獲得鎖之后,其他線程將會一直循環(huán)在哪里查看是否該鎖被釋放斩狱。所以耳高,此鎖比較適用于鎖的持有者保存時間較短的情況下。
只有加鎖所踊,解鎖泌枪,嘗試加鎖三個方法。
相關(guān)API:
typedef int32_t OSSpinLock;
// 加鎖
void OSSpinLockLock( volatile OSSpinLock *__lock );
// 嘗試加鎖
bool OSSpinLockTry( volatile OSSpinLock *__lock );
// 解鎖
void OSSpinLockUnlock( volatile OSSpinLock *__lock );
用法:
- (void)osspinlock {
__block OSSpinLock theLock = OS_SPINLOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
NSLog(@"線程1開始");
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);
});
OSSpinLock 在iOS 10.0中被 <os/lock.h> 中的 os_unfair_lock 取代,同時OSSpinLock存在不安全性
6秕岛、os_unfair_lock
自旋鎖已經(jīng)不再安全碌燕,然后蘋果又整出來個 os_unfair_lock,這個鎖解決了優(yōu)先級反轉(zhuǎn)問題继薛。
常用相關(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 基本一直修壕,就不一一列出了。
全文總結(jié)
應(yīng)當(dāng)針對不同的操作使用不同的鎖遏考,而不能一概而論哪種鎖的加鎖解鎖速度快慈鸠。
1. 其實每一種鎖基本上都是加鎖、等待灌具、解鎖的步驟青团,理解了這三個步驟就可以幫你快速的學(xué)會各種鎖的用法譬巫。
2. @synchronized 的效率最低,不過它的確用起來最方便督笆,所以如果沒什么性能瓶頸的話芦昔,可以選擇使用 @synchronized。
3. 當(dāng)性能要求較高時候胖腾,可以使用 pthread_mutex 或者 dispath_semaphore烟零,由于 OSSpinLock 不能很好的保證線程安全,而在只有在 iOS10 中才有 os_unfair_lock 咸作,所以锨阿,前兩個是比較好的選擇。既可以保證速度记罚,又可以保證線程安全墅诡。
文章開端已經(jīng)放了一張各個鎖的性能圖,大家可以根據(jù)項目需要選擇適合鎖桐智。