在平時的開發中經常使用到多線程,在使用多線程的過程中,難免會遇到資源競爭的問題,那我們怎么來避免出現這種問題那?
線程安全是什么?
當一個線程訪問數據的時候,其他的線程不能對其進行訪問,直到該線程訪問完畢。簡單來講就是在同一時刻,對同一個數據操作的線程只有一個。只有確保了這樣,才能使數據不會被其他線程影響。而線程不安全,則是在同一時刻可以有多個線程對該數據進行訪問,從而得不到預期的結果。
比如寫文件和讀文件,當一個線程在寫文件的時候,理論上來說,如果這個時候另一個線程來直接讀取的話,那么得到的結果可能是你無法預料的。
怎么來保證線程安全?
通常我們使用鎖的機制來保證線程安全,即確保同一時刻只有同一個線程來對同一個數據源進行訪問。
YY大神 的 不再安全的 OSSpinLock 這邊博客中列出了各種鎖以及性能比較:
性能對比
這里性能比較的只是加鎖立馬解鎖的時間消耗,并沒有計算競爭時候的時間消耗。
鎖的介紹及簡單使用
1.@synchronized
@synchronized是 iOS 中最常見的鎖,用法很簡單:
- (void)viewDidLoad { [super viewDidLoad]; [self synchronized];}- (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結束"); } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); @synchronized(cjobj){ NSLog(@"線程2"); } });}
控制臺輸出:
2017-10-18 11:35:13.459194+0800 Thread-Lock[24855:431100] 線程1開始
2017-10-18 11:35:16.460210+0800 Thread-Lock[24855:431100] 線程1結束
2017-10-18 11:35:16.460434+0800 Thread-Lock[24855:431101] 線程2
從上面的控制臺輸出時間可以看出來,在線程 1 內容全部輸出之后,才輸出了線程 2 的內容,“線程1結束”與“線程2”都是在“線程1開始”3 秒后輸出的。
@synchronized(cjobj) 指令使用的 cjobj 為該鎖的唯一標識,只有當標識相同時,才為滿足互斥,如果線程 2 中的 @synchronized(cjobj) 改為 @synchronized(self) ,那么線程 2 就不會被阻塞,@synchronized 指令實現鎖的優點就是我們不需要在代碼中顯式的創建鎖對象,便可以實現鎖的機制,但作為一種預防措施,@synchronized 塊會隱式的添加一個異常處理例程來保護代碼,該處理例程會在異常拋出的時候自動的釋放互斥鎖。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖對象。
@sychronized(cjobj){} 內部 cjobj 被釋放或被設為 nil 不會影響鎖的功能,但如果 cjobj 一開始就是 nil,那就會丟失了鎖的功能了。
2.NSLock
先看看iOS中NSLock類的.h文件,從代碼中可以看出,該類分成了幾個子類:NSLock、NSConditionLock、NSRecursiveLock、NSCondition,然后有一個 NSLocking 協議:
@protocol NSLocking- (void)lock;- (void)unlock;@end
雖然 NSLock、NSConditionLock、NSRecursiveLock、NSCondition 都遵循的了 NSLocking 協議,但是它們并不相同。
2.1 NSLock
NSLock 實現了最基本的互斥鎖,遵循了 NSLocking 協議,通過 lock 和 unlock 來進行鎖定和解鎖。
源碼內容:
@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)viewDidLoad { [super viewDidLoad]; [self nslock];}- (void)nslock { NSLock * cjlock = [NSLock new]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [cjlock lock]; NSLog(@"線程1加鎖成功"); sleep(2); [cjlock unlock]; NSLog(@"線程1解鎖成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); [cjlock lock]; NSLog(@"線程2加鎖成功"); [cjlock unlock]; NSLog(@"線程2解鎖成功"); });}
控制臺輸出:
2017-10-19 15:03:58.868708+0800 Thread-Lock[39059:846493] 線程1加鎖成功
2017-10-19 15:04:00.872714+0800 Thread-Lock[39059:846493] 線程1解鎖成功
2017-10-19 15:04:00.872722+0800 Thread-Lock[39059:846492] 線程2加鎖成功
2017-10-19 15:04:00.873000+0800 Thread-Lock[39059:846492] 線程2解鎖成功
- (void)nslock { NSLock * cjlock = [NSLock new]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [cjlock lock]; NSLog(@"線程1加鎖成功"); sleep(2); [cjlock unlock]; NSLog(@"線程1解鎖成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if ([cjlock tryLock]) { NSLog(@"線程3加鎖成功"); [cjlock unlock]; NSLog(@"線程3解鎖成功"); }else { NSLog(@"線程3加鎖失敗"); } });}
控制臺輸出:
2017-10-19 15:05:38.627767+0800 Thread-Lock[39118:849171] 線程1加鎖成功
2017-10-19 15:05:38.627767+0800 Thread-Lock[39118:849169] 線程3加鎖失敗
2017-10-19 15:05:40.629969+0800 Thread-Lock[39118:849171] 線程1解鎖成功
- (void)nslock { NSLock * cjlock = [NSLock new]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [cjlock lock]; NSLog(@"線程1加鎖成功"); sleep(2); [cjlock unlock]; NSLog(@"線程1解鎖成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(3); if ([cjlock tryLock]) { NSLog(@"線程4加鎖成功"); [cjlock unlock]; NSLog(@"線程4解鎖成功"); }else { NSLog(@"線程4加鎖失敗"); } });}
控制臺輸出:
2017-10-19 15:07:14.872279+0800 Thread-Lock[39166:851060] 線程1加鎖成功
2017-10-19 15:07:16.876108+0800 Thread-Lock[39166:851060] 線程1解鎖成功
2017-10-19 15:07:17.876208+0800 Thread-Lock[39166:851052] 線程4加鎖成功
2017-10-19 15:07:17.876527+0800 Thread-Lock[39166:851052] 線程4解鎖成功
- (void)nslock { NSLock * cjlock = [NSLock new]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [cjlock lock]; NSLog(@"線程1加鎖成功"); sleep(2); [cjlock unlock]; NSLog(@"線程1解鎖成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if ([cjlock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) { NSLog(@"線程5加鎖成功"); [cjlock unlock]; NSLog(@"線程5解鎖成功"); }else { NSLog(@"線程5加鎖失敗"); } });}
控制臺輸出:
2017-10-19 15:08:39.705131+0800 Thread-Lock[39204:852782] 線程1加鎖成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852782] 線程1解鎖成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852784] 線程5加鎖成功
2017-10-19 15:08:41.708935+0800 Thread-Lock[39204:852784] 線程5解鎖成功
注意:lock與unlock操作必須在同一線程,否則結果不確定甚至會引起死鎖
由以上內容總結:
2.2 NSRecursiveLock
NSRecursiveLock 是遞歸鎖,顧名思義,可以被一個線程多次獲得,而不會引起死鎖。它記錄了成功獲得鎖的次數,每一次成功的獲得鎖,必須有一個配套的釋放鎖和其對應,這樣才不會引起死鎖。NSRecursiveLock 會記錄上鎖和解鎖的次數,當二者平衡的時候,才會釋放鎖,其它線程才可以上鎖成功。
源碼內容:
@interface NSRecursiveLock : 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)viewDidLoad { [super viewDidLoad]; [self nsrecursivelock];}- (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); });}
控制臺輸出:
2017-10-19 16:15:40.584213+0800 Thread-Lock[39579:894111] 3加鎖成功
2017-10-19 16:15:40.584387+0800 Thread-Lock[39579:894111] value:3
2017-10-19 16:15:40.584552+0800 Thread-Lock[39579:894111] 2加鎖成功
2017-10-19 16:15:40.584635+0800 Thread-Lock[39579:894111] value:2
2017-10-19 16:15:40.584810+0800 Thread-Lock[39579:894111] 1加鎖成功
2017-10-19 16:15:40.585267+0800 Thread-Lock[39579:894111] value:1
2017-10-19 16:15:40.585714+0800 Thread-Lock[39579:894111] 0加鎖成功
2017-10-19 16:15:40.585906+0800 Thread-Lock[39579:894111] 0解鎖成功
2017-10-19 16:15:40.586138+0800 Thread-Lock[39579:894111] 1解鎖成功
2017-10-19 16:15:40.586217+0800 Thread-Lock[39579:894111] 2解鎖成功
2017-10-19 16:15:40.586314+0800 Thread-Lock[39579:894111] 3解鎖成功
由以上內容總結:
如果用 NSLock 的話,cjlock 先鎖上了,但未執行解鎖的時候,就會進入遞歸的下一層,而再次請求上鎖,阻塞了該線程,線程被阻塞了,自然后面的解鎖代碼不會執行,而形成了死鎖。而 NSRecursiveLock 遞歸鎖就是為了解決這個問題。
2.3 NSConditionLock
NSConditionLock 對象所定義的互斥鎖可以在使得在某個條件下進行鎖定和解鎖,它和 NSLock 類似,都遵循 NSLocking 協議,方法都類似,只是多了一個 condition 屬性,以及每個操作都多了一個關于 condition 屬性的方法,例如 tryLock、tryLockWhenCondition:,所以 NSConditionLock 可以稱為條件鎖。
源碼內容:
@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)viewDidLoad { [super viewDidLoad]; [self nsconditionlock];}- (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解鎖成功
由以上內容總結:
2.4、NSCondition
NSCondition 是一種特殊類型的鎖,通過它可以實現不同線程的調度。一個線程被某一個條件所阻塞,直到另一個線程滿足該條件從而發送信號給該線程使得該線程可以正確的執行。比如說,你可以開啟一個線程下載圖片,一個線程處理圖片。這樣的話,需要處理圖片的線程由于沒有圖片會阻塞,當下載線程下載完成之后,則滿足了需要處理圖片的線程的需求,這樣可以給定一個信號,讓處理圖片的線程恢復運行。
源碼內容:
@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)viewDidLoad { [super viewDidLoad]; [self nscondition];}- (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]; });}
控制臺輸出:
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線程解鎖
//如果 [cjcondition signal]; 改成 [cjcondition broadcast]; dispatch_async(dispatch_get_global_queue(0, 0), ^{ sleep(2); [cjcondition broadcast]; });
控制臺輸出:
2017-10-19 17:18:08.054109+0800 Thread-Lock[40056:946099] 線程1線程加鎖
2017-10-19 17:18:08.054304+0800 Thread-Lock[40056:946096] 線程2線程加鎖
2017-10-19 17:18:10.056071+0800 Thread-Lock[40056:946099] 線程1線程喚醒
2017-10-19 17:18:10.056231+0800 Thread-Lock[40056:946099] 線程1線程解鎖
2017-10-19 17:18:10.056244+0800 Thread-Lock[40056:946096] 線程2線程喚醒
2017-10-19 17:18:10.056445+0800 Thread-Lock[40056:946096] 線程2線程解鎖
由以上內容總結:
3.dispatch_semaphore
dispatch_semaphore 使用信號量機制實現鎖,等待信號和發送信號。
常用相關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)viewDidLoad { [super viewDidLoad]; [self dispatch_semaphore];}- (void)dispatch_semaphore { dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); 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結束"); 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結束
2017-10-19 18:30:42.674165+0800 Thread-Lock[40569:993612] 線程2開始
//如果 overTime 改成 3 秒
控制臺輸出:
2017-10-19 18:32:32.078186+0800 Thread-Lock[40634:995921] 線程1開始
2017-10-19 18:32:35.082943+0800 Thread-Lock[40634:995920] 線程2開始
2017-10-19 18:32:37.083115+0800 Thread-Lock[40634:995921] 線程1結束
由以上內容總結:
4.pthread_mutex 與 pthread_mutex(recursive)
pthread 表示 POSIX thread,定義了一組跨平臺的線程相關的 API,POSIX 互斥鎖是一種超級易用的互斥鎖,使用的時候:
常用相關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);pthread_mutex_destroy(pthread_mutex_t * _Nonnull);
用法:
//pthread_mutex- (void)viewDidLoad { [super viewDidLoad]; [self pthread_mutex];}- (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結束"); 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); });}
控制臺輸出:
2017-10-23 14:50:29.842180+0800 Thread-Lock[74478:1647362] 線程1開始
2017-10-23 14:50:32.846786+0800 Thread-Lock[74478:1647362] 線程1結束
2017-10-23 14:50:32.847001+0800 Thread-Lock[74478:1647359] 線程2
//pthread_mutex(recursive)- (void)viewDidLoad { [super viewDidLoad]; [self pthread_mutex_recursive];}- (void)pthread_mutex_recursive { __block pthread_mutex_t cjlock; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&cjlock, &attr); pthread_mutexattr_destroy(&attr); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ static void (^RecursiveBlock)(int); RecursiveBlock = ^(int value) { pthread_mutex_lock(&cjlock); NSLog(@"%d加鎖成功",value); if (value > 0) { NSLog(@"value = %d", value); sleep(1); RecursiveBlock(value - 1); } NSLog(@"%d解鎖成功",value); pthread_mutex_unlock(&cjlock); }; RecursiveBlock(3); });}
//控制臺輸出:
2017-10-23 15:31:51.599693+0800 Thread-Lock[74723:1668089] 3加鎖成功
2017-10-23 15:31:51.599912+0800 Thread-Lock[74723:1668089] value = 3
2017-10-23 15:31:52.602002+0800 Thread-Lock[74723:1668089] 2加鎖成功
2017-10-23 15:31:52.602317+0800 Thread-Lock[74723:1668089] value = 2
2017-10-23 15:31:53.604669+0800 Thread-Lock[74723:1668089] 1加鎖成功
2017-10-23 15:31:53.604957+0800 Thread-Lock[74723:1668089] value = 1
2017-10-23 15:31:54.607778+0800 Thread-Lock[74723:1668089] 0加鎖成功
2017-10-23 15:31:54.608109+0800 Thread-Lock[74723:1668089] 0解鎖成功
2017-10-23 15:31:54.608391+0800 Thread-Lock[74723:1668089] 1解鎖成功
2017-10-23 15:31:54.608622+0800 Thread-Lock[74723:1668089] 2解鎖成功
2017-10-23 15:31:54.608945+0800 Thread-Lock[74723:1668089] 3解鎖成功
由以上內容總結:
5. OSSpinLock
OSSpinLock 是一種自旋鎖,和互斥鎖類似,都是為了保證線程安全的鎖。但二者的區別是不一樣的,對于互斥鎖,當一個線程獲得這個鎖之后,其他想要獲得此鎖的線程將會被阻塞,直到該鎖被釋放。但自選鎖不一樣,當一個線程獲得鎖之后,其他線程將會一直循環在哪里查看是否該鎖被釋放。所以,此鎖比較適用于鎖的持有者保存時間較短的情況下。
只有加鎖,解鎖,嘗試加鎖三個方法。
常用相關API:
typedef int32_t OSSpinLock;// 加鎖void OSSpinLockLock( volatile OSSpinLock *__lock );// 嘗試加鎖bool OSSpinLockTry( volatile OSSpinLock *__lock );// 解鎖void OSSpinLockUnlock( volatile OSSpinLock *__lock );
用法:
#import <libkern/OSAtomic.h>- (void)viewDidLoad { [super viewDidLoad]; [self osspinlock];}- (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結束"); OSSpinLockUnlock(&theLock); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ OSSpinLockLock(&theLock); sleep(1); NSLog(@"線程2"); OSSpinLockUnlock(&theLock); });}
控制臺輸出:
2017-10-23 16:02:48.865501+0800 Thread-Lock[75025:1684487] 線程1開始
2017-10-23 16:02:51.868736+0800 Thread-Lock[75025:1684487] 線程1結束
2017-10-23 16:02:52.922911+0800 Thread-Lock[75025:1684486] 線程2
YY大神 @ibireme 的文章也有說這個自旋鎖存在優先級反轉問題,具體文章可以戳 不再安全的 OSSpinLock,而 OSSpinLock 在iOS 10.0中被 <os/lock.h> 中的 os_unfair_lock 取代。
6.os_unfair_lock
自旋鎖已經不再安全,然后蘋果又整出來個 os_unfair_lock,這個鎖解決了優先級反轉問題。
常用相關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 基本一直,就不一一列出了。
總結
應當針對不同的操作使用不同的鎖,而不能一概而論哪種鎖的加鎖解鎖速度快。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答