首先理解幾個鎖的概念:
互斥鎖(mutexlock)sleep-waiting:
保證共享數據操作的完整性, 鎖被佔用的時候會休眠, 等待鎖釋放的時候會喚醒。
在訪問共享資源之前進行加鎖,訪問完成後解鎖。
加鎖後,任何其他試圖加鎖的線程會被阻塞,直到當前線程解鎖。
解鎖時,如果有1個以上的線程阻塞,那麼所有該鎖上的線程變爲就緒狀態,第一個就緒的加鎖,其他的又進入休眠。
從而實現在任意時刻,最多隻有1個線程能夠訪問被互斥鎖保護的資源。
自旋鎖(spinlock)busy-waiting:
跟互斥類似, 只是資源被佔用的時候, 會一直循環檢測鎖是否被釋放(CPU不能做其他的事情)
節省了喚醒睡眠線程的內核消耗(在加鎖時間短暫的情況下會大大提高效率)
讀寫鎖(rwlock):
顧名思義 - 寫的時候:讀寫都等待;讀的時候:寫等待,讀無需等待
適合讀次數遠遠大於寫的情況 (爲了公平,應該設計爲:寫操作到來時,後面的讀阻塞)
條件鎖(condlock):
線程會因爲條件變量不滿足而阻塞,線程也可以在釋放鎖時將條件變量改成某個值,從而喚醒滿足條件變量的線程
遞歸鎖(recursivelock):
跟互斥類似, 但是允許同一個線程在未釋放鎖前,加鎖N次鎖, 不會引發死鎖
死鎖:
這裏給出的是同一個線程,在解鎖之前,多次上鎖的例子:會導致死鎖。下面任意一個遞歸鎖都可以解決這個導致死鎖的問題。
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"加鎖 %d", value);
sleep(2);
RecursiveMethod(value - 1); // 解鎖之前又加想鎖, 需要等待鎖的解除,
}
NSLog(@"解鎖 %d", value);
[lock unlock];
};
RecursiveMethod(5);
});
接下來按目前的耗時排序介紹,先耗時短的:
1、OSSpinLock 自旋鎖
會造成優先級反轉的問題,iOS10+已廢棄,有興趣可以看看:不再去安全的OSSpinLock。這裏也給出一下使用例子:
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
__block OSSpinLock osslock = OS_SPINLOCK_INIT; // 初始化
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&osslock); // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
OSSpinLockUnlock(&osslock); // 解鎖
});
}
2、os_unfair_lock 互斥鎖 iOS10+
iOS10+才支持,爲了代替OSSpinLock,需要: #import <os/lock.h>。使用例子:
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT); // 必須在線程裏初始化
os_unfair_lock_lock(unfairLock); // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"線程1 資源1: %@", self.items);
os_unfair_lock_unlock(unfairLock); // 解鎖
});
}
3、dispatch_semaphore 信號量
保證關鍵代碼不併發執行。使用例子:
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
dispatch_semaphore_signal(semaphore); // 解鎖
});
}
4、pthread_mutex 互斥鎖
蘋果做出了優化, 性能不比semaphore差, 而且肯定安全(它有兩種初始化方法,第二種帶參數的可以設置type,下面會介紹)。需要:#import <pthread.h>。使用例子:
pthread_mutex_t _pthread;
pthread_mutex_init(&_pthread, NULL); // _pthread 不能是局部變量!!
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&_pthread); // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
pthread_mutex_unlock(&_pthread); // 解鎖
});
}
5、NSLock 互斥鎖
很方便,競爭的是這個鎖對象。使用例子:
self.lock = [[NSLock alloc] init];
self.lock.name = @"itemsLock";
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.lock lock]; // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
[self.lock unlock]; // 解鎖
});
}
6、NSCondition 條件鎖
內部有實現NSLocking協議, 就能實現NSLock所有功能。使用例子:
NSCondition *lock = [[NSCondition alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 1");
[lock lock];
// [lock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; // 讓當前線程等待一段時間
[lock wait];
NSLog(@"end 1");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 2");
[lock lock];
[lock wait];
NSLog(@"end 2");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start 3");
sleep(2);
// [lock signal]; // 喚醒一個等待的線程
[lock broadcast]; // 喚醒所有等待的線程
NSLog(@"end 3");
});
7、pthread_mutex(recursive) 遞歸鎖
需要設置 type = PTHREAD_MUTEX_RECURSIVE。
pthread_mutex_t plock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr); // 初始化attr, 並賦予默認
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 設置type爲遞歸鎖
pthread_mutex_init(&plock, &attr); // 若爲 pthread_mutex_init(&plock, NULL) 則會死鎖
pthread_mutexattr_destroy(&attr); // 銷燬一個屬性對象,在重新初始化之前該結構不能重複使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
NSLog(@"加鎖: %d", value);
pthread_mutex_lock(&plock); // 加鎖
if (value < 5) {
sleep(2);
RecursiveMethod(++value); // 解鎖之前又加想鎖, 需要等待鎖的解除,
}
NSLog(@"解鎖: %d", value);
pthread_mutex_unlock(&plock); // 解鎖
};
RecursiveMethod(1);
NSLog(@"finish");
});
atrr的始終類型:
- PTHREAD_MUTEX_NORMAL: 互斥鎖不會檢測死鎖。
當一個線程解鎖之前又鎖上,將導致死鎖。
嘗試解除其他線程上的鎖,結果不可預測。
嘗試解除一個未鎖定的鎖,結果不可預測。
- PTHREAD_MUTEX_ERRORCHECK: 互斥鎖提供錯誤檢查。
當一個線程嘗試重新鎖定一個還未解開的鎖時,將會返回一個錯誤。
嘗試解除其他線程上的鎖,將會返回一個錯誤。
嘗試解除一個未鎖定的鎖,將會返回一個錯誤。
- PTHREAD_MUTEX_RECURSIVE: 遞歸鎖
一個線程可以多次鎖定一個還未解開的鎖,需要相同數量的解鎖來釋放鎖,然後另一個線程才能獲的互斥鎖
嘗試解除其他線程上的鎖,將會返回一個錯誤。
嘗試解除一個未鎖定的鎖,將會返回一個錯誤。
- PTHREAD_MUTEX_DEFAULT:
嘗試遞歸鎖定此類型的鎖,結果不可預測。
嘗試解除其他線程上的鎖,結果不可預測。
嘗試解除一個未鎖定的鎖,結果不可預測。
8、NSRecursiveLock 遞歸鎖
直接操作lock對象很方便,競爭的是這個鎖對象。使用例子:
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
// [lock tryLock];
// [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
NSLog(@"加鎖: %d", value);
[lock lock]; // 加鎖
if (value < 5) {
sleep(2);
RecursiveMethod(++value); // 解鎖之前又鎖上
}
NSLog(@"解鎖: %d", value);
[lock unlock]; // 解鎖
};
RecursiveMethod(1);
NSLog(@"finish");
});
9、NSConditionLock 條件鎖
NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0]; // 設置初始標誌位
// [cLock lock]; // 不區分條件, 可加鎖就執行
// [cLock unlock]; // 不會清空條件, 之後滿足條件的鎖還會執行
// [cLock tryLockWhenCondition:n]; // 標誌位爲n時, 加鎖成功
// [cLock unlockWithCondition:n]; // 解鎖, 並設置標誌位
// [cLock lockWhenCondition:n]; // 標誌位爲n時加鎖, 執行之後代碼
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([cLock tryLockWhenCondition:0]) {
NSLog(@"任務1");
[cLock unlockWithCondition:1];
} else {
NSLog(@"任務1 加鎖失敗");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:3];
NSLog(@"任務2");
[cLock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[cLock lockWhenCondition:1];
NSLog(@"任務3");
[cLock unlockWithCondition:3];
});
10、@synchronized 互斥鎖
用pthread_mutex_t實現的。
NSArray *items = @[@"1", @"2", @"3"];
self.items = [[NSMutableArray alloc] init];
for (int i = 0; i < items.count; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (self.items) { // 加鎖
sleep(items.count - i);
[self.items addObject:items[i]];
NSLog(@"%@", self.items);
}
});
}
11、POSIXConditions 條件鎖:互斥鎖 + 條件鎖
參考 線程被一個 互斥 和 條件 結合的信號來喚醒,當鎖和條件同時滿足時,才能執行:
- (void)POSIX_Codictions {
ready_to_go = false;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condition, NULL);
[self waitOnConditionFunction];
[self signalThreadUsingCondition];
}
- (void)waitOnConditionFunction {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex); // Lock the mutex.
while(ready_to_go == false) {
NSLog(@"wait...");
pthread_cond_wait(&condition, &mutex); // 休眠
}
NSLog(@"done");
ready_to_go = false;
pthread_mutex_unlock(&mutex);
});
}
- (void)signalThreadUsingCondition {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex); // Lock the mutex.
ready_to_go = true;
NSLog(@"true");
pthread_cond_signal(&condition); // Signal the other thread to begin work.
pthread_mutex_unlock(&mutex);
});
}
12、pthreadReadWrite 讀寫鎖
__block pthread_rwlock_t rwLock;
pthread_rwlock_init(&rwLock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_wrlock(&rwLock);
NSLog(@"3 寫 begin");
sleep(3);
NSLog(@"3 寫 end");
pthread_rwlock_unlock(&rwLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_rdlock(&rwLock);
NSLog(@"1 讀 begin");
sleep(1);
NSLog(@"1 讀 end");
pthread_rwlock_unlock(&rwLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_rwlock_rdlock(&rwLock);
NSLog(@"2 讀 begin");
sleep(2);
NSLog(@"2 讀 end");
pthread_rwlock_unlock(&rwLock);
});