多線程模型下,因爲共享內存帶來的衝突風險,鎖是個避不開的話題。java
首先從平臺無關的角度看,從能力上區分,主要有如下幾種鎖:ios
全部的鎖,語義上基本就這幾種,git
以API提供者的維度梳理一下iOS的鎖github
內核安全
pthread:POSIX標準真的是大而全...啥都有多線程
GCD併發
Cocoa Foundationasync
objc runtime函數
以上,相對全面地列舉了iOS中的鎖,它們是不一樣層級的庫提供的,但因爲iOS中全部的線程本質上都是內核級線程,所以這些鎖是可以公用的。post
環境:iPhone 7 plus + iOS 11
基於YY老師 再也不安全的 OSSpinLock 中的性能對比代碼,加入了os_unfair_lock
,從新跑的一個性能對比。測試代碼在這裏
上面測試的是純粹的加鎖解鎖性能,中間沒有任何邏輯也不存在多線程搶佔,爲了更貼合咱們的實際環境,我構造了一個簡單的多線程環境:NSOperationQueue
最大併發數爲10,建立10個NSOperation
,每一個NSOperation
作10w次i++操做,每次操做加鎖,代碼在這裏,結果以下:
能夠看到多線程搶佔的情形下結果跟前面略有不一樣,在真實業務場景下這個數據應該更有參考意義。
因爲OSSpinLock存在的優先級反轉問題,已經廢棄再也不使用。(參考:再也不安全的 OSSpinLock )
@synchronized
。使用最方便。通常業務開發場景,鎖的性能影響不大,能力上也只須要簡單的互斥鎖,所以怎麼方便怎麼來。並且@synchronized
性能也沒有差太多。os_unfair_lock
,自旋鎖廢棄後官方推薦的替代品,性能優異。dispatch_semaphore
NSCondition
pthread_rwlock
NSRecursiveLock
自旋鎖是這些鎖中惟一一個依靠忙等待實現的鎖,也就是說能夠理解成一個暴力的while循環,所以會浪費較多的CPU,但它是全部鎖中性能最高的。適用於對時延要求比較苛刻、臨界區計算量比較小、自己CPU不存在瓶頸的場景。
可是如今不能用了。YY老師在再也不安全的 OSSpinLock 中講得很清楚了,當低優先級的線程已進入臨界區,高優先級的線程想要獲取資源就須要忙等待,佔用大量CPU,致使低優先級線程遲遲不能執行完臨界區代碼,致使類死鎖的問題。
OSSpinLock lock = OS_SPINLOCK_INIT; OSSpinLockLock(&lock); // do something OSSpinLockUnlock(&lock);
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; os_unfair_lock_lock(&lock); // do something os_unfair_lock_unlock(&lock);
pthread_mutex_t lock; pthread_mutex_init(&lock, NULL); pthread_mutex_lock(&lock); // do something pthread_mutex_unlock(&lock);
dispatch_semaphore_t lock = dispatch_semaphore_create(1); dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); // do something dispatch_semaphore_signal(lock);
dispatch_semaphore_create
傳入參數是信號量的值,在這裏就是可以同時進入臨界區的線程數。dispatch_semaphore_wait
,當信號量大於0時減一併進入臨界區,若是信號量等於0則等待直到信號量不爲0或到達設定時間。dispatch_semaphore_signal
使信號量加1。
NSLock *lock = [NSLock new]; [lock lock]; // do something [lock unlock];
條件鎖,以生產者消費者模型爲例
NSCondition *condition = [NSCondition new]; // Thread 1: 消費者 - (void)consumer { [condition lock]; while(conditionNotSatisfied){ [condition wait] } // 消費邏輯 consume(); [condition unlock]; } // Thread 2: 生產者 - (void)producer { [condition lock]; // 生產邏輯 produce(); [condition signal]; [condition unlock]; }
條件鎖,跟NSCondition差很少,對條件作了封裝,簡化了使用但也沒NSCondition那麼靈活了。
NSConditionLock *condition = [[NSConditionLock alloc] initWithCondition:1]; // Thread 1: 消費者 - (void)consumer { [condition lockWhenCondition:1]; while(conditionNotSatisfied){ [condition wait] } // 消費邏輯 consume(); [condition unlockWithCondition:0]; } // Thread 2: 生產者 - (void)producer { [condition lock]; // 生產邏輯 produce(); [condition unlockWithCondition:1]; }
能夠遞歸調用的互斥鎖。
int i = 0; NSRecursiveLock *lock = [NSRecursiveLock new]; - (void)testLock { if(i > 0){ [lock lock]; [self testLock]; i --; [lock lock]; } }
普通的鎖,用着方便。
@synchronized(self) { // do something }
讀寫鎖,通常也不怎麼用得上,這裏給了個字典set/get的例子,可是實際業務場景,一般普通的互斥鎖就能夠了。
在讀操做比寫操做多不少的狀況下,讀寫鎖的收益比較可觀。
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; NSMutableDictionary *dic = [NSMutableDictionary new]; - (void)set { // 寫模式加鎖 pthread_rwlock_wrlock(&lock); dic[@"key"] = @"value"; // 解鎖 pthread_rwlock_unlock(&lock); } - (NSString *)get { NSString *value; // 寫模式加鎖 pthread_rwlock_rdlock(&lock); value = dic[@"key"]; // 解鎖 pthread_rwlock_unlock(&lock); return value; }
推薦閱讀