主要講解OSSpinLock/os_unfair_lock/pthread_mutex_t鎖的基本用法
常見鎖的分類:
- 自旋鎖
OSSpinLock
- 互斥鎖
os_unfair_lock
- 互斥/遞歸/條件鎖
pthread_mutex_t
- 互斥鎖
NSLock
- 遞歸鎖
NSRecursiveLock
- 條件鎖
NSCondition
- 條件鎖
NSConditionLock
- 遞歸鎖
@synchronized
- 信號量
semaphore
- 讀寫鎖
pthread_rwlock_t
- 異步柵欄
dispatch_barrier_async
iOS 鎖 部分一
iOS 鎖 部分二
iOS 鎖 部分三
iOS 鎖 部分四
1. 自旋鎖 OSSpinLock
特點:
- 不是一個安全的鎖, 等待鎖的線程會處于忙等(
busy-wait
)狀態(tài), 一直占用著CPU
資源; (類似一個while(1)
循環(huán)一樣,不停的查詢鎖的狀態(tài), 注意區(qū)分Runloop
的機(jī)制, 同樣是阻塞, 但是Runloop
是類似休眠的阻塞, 不會耗費(fèi)CPU
資源, 自旋鎖的這種忙等機(jī)制使它相比其他鎖效率更高, 因為沒有喚醒-休眠這些類似操作, 從而能更快去處理事情);
注意
OSSpinLock
自旋鎖iOS10
之后已經(jīng)廢棄使用, 它可能會導(dǎo)致優(yōu)先級反轉(zhuǎn); 例如A/B兩個線程, A的優(yōu)先級大于B的, 我們的本意是A的任務(wù)優(yōu)先執(zhí)行; 但是使用OSSpinLock
后, 如果是B先獲得了優(yōu)先訪問了共享資源獲得了鎖并加鎖, 而A線程再去訪問共享資源時鎖就會處于忙等狀態(tài), 由于優(yōu)先級高則它會一直占用CPU
資源不會讓出時間片;這樣B一直不能獲得CPU
資源去執(zhí)行任務(wù), 導(dǎo)致無法完成;
- 使用時需要導(dǎo)入頭文件
#import <libkern/OSAtomic.h>
- 使用時可能會用到的方法:
3.1OS_SPINLOCK_INIT
初始化自旋鎖;
3.2OSSpinLockLock()
加鎖;
3.3OSSpinLockUnlock()
解鎖;
3.4OSSpinLockTry()
嘗試加鎖, 返回一個BOOL
值; - 示例代碼如下
#import "ViewController1.h"
#import <libkern/OSAtomic.h>
@interface ViewController1 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) OSSpinLock lock;
@end
@implementation ViewController1
- (void)viewDidLoad {
[super viewDidLoad];
self.money = 100;
self.lock = OS_SPINLOCK_INIT;
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf drawMoney];
}
});
}
- (void)saveMoney{
//加鎖
OSSpinLockLock(&_lock);
[super saveMoney];
//解鎖
OSSpinLockUnlock(&_lock);
}
- (void)drawMoney {
//加鎖
OSSpinLockLock(&_lock);
[super drawMoney ];
//解鎖
OSSpinLockUnlock(&_lock);
}
@end
2. 互斥鎖 os_unfair_lock
特點
- 設(shè)計宗旨在于替換
OSSpinLock
, 從iOS10
之后開始支持; 跟OSSpinLock
不同, 等待os_unfari_lock
的線程會處于休眠狀態(tài)(類似Runloop
那這樣), 不是忙等; - 使用時需要引入頭文件
#import <os/lock.h>
- 使用時可能會用到的方法
3.1OS_UNFAIR_LOCK_INIT
初始化鎖;
3.2os_unfair_lock_lock()
加鎖;
3.3os_unfair_lock_unlock()
解鎖;
3.4os_unfair_lock_trylock()
嘗試加鎖, 返回一個BOOL
值; - 測試代碼
#import "ViewController2.h"
#import <os/lock.h>
@interface ViewController2 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) os_unfair_lock lock;
@end
@implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
self.money = 100;
self.lock = OS_UNFAIR_LOCK_INIT;
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf drawMoney];
}
});
}
- (void)saveMoney{
os_unfair_lock_lock(&_lock);
[super saveMoney];
os_unfair_lock_unlock(&_lock);
}
- (void)drawMoney {
os_unfair_lock_lock(&_lock);
[super drawMoney ];
os_unfair_lock_unlock(&_lock);
}
@end
3. 互斥/遞歸/條件鎖 pthread_mutex_t
特點
- 跨平臺使用的
API
, 等待鎖的線程會處于休眠狀態(tài);
當(dāng)使用遞歸鎖時: 允許同一個線程重復(fù)進(jìn)行加鎖(另一個線程訪問時就會等待), 這樣可以保證多線程時訪問共用資源的安全性; - 使用時需要引入頭文件
#import <pthread.h>
- 使用時可能會用到的方法
3.1 鎖的類型有
///普通互斥鎖
#define PTHREAD_MUTEX_NORMAL 0
///錯誤檢查鎖(不清楚什么用)
#define PTHREAD_MUTEX_ERRORCHECK 1
///遞歸鎖
#define PTHREAD_MUTEX_RECURSIVE 2
///等同PTHREAD_MUTEX_NORMAL
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
3.2 鎖的初始化方法:
///初始化屬性
pthread_mutexattr_t att ;
pthread_mutexattr_init(&att);
///設(shè)置鎖的屬性
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
///初始化鎖
pthread_mutex_init(&_lock, &att);
3.2 pthread_mutex_lock()
加鎖
3.3 pthread_mutex_unlock()
解鎖
3.4 pthread_mutexattr_destroy()
銷毀屬性;
3.5 pthread_mutex_destroy()
銷毀鎖;
3.6 pthread_cond_wait()
等待條件(進(jìn)入休眠的同時放開 mutex 鎖, 被喚醒的同時再次對 mutex 加鎖)
3.7 pthread_cond_signal()
發(fā)送信號激活等待該條件的線程;
3.8 pthread_cond_broadcast()
發(fā)送廣播信號激活等待該條件的所有線程;
- 關(guān)于
互斥鎖
和遞歸鎖
的測試代碼:
#import "ViewController3.h"
#import <pthread.h>
@interface ViewController3 ()
@property (nonatomic, assign) NSInteger money;
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) pthread_mutex_t recursiveLock;
@end
@implementation ViewController3
- (void)viewDidLoad {
[super viewDidLoad];
/******************************互斥鎖驗證******************************/
self.money = 100;
///初始化屬性
pthread_mutexattr_t att ;
pthread_mutexattr_init(&att);
///設(shè)置鎖的屬性
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
///初始化鎖
pthread_mutex_init(&_lock, &att);
///銷毀屬性
pthread_mutexattr_destroy(&att);
[self hanleMoney];
/******************************遞歸鎖驗證******************************/
///初始化屬性
pthread_mutexattr_t recursiveAtt ;
pthread_mutexattr_init(&recursiveAtt);
///設(shè)置鎖的屬性
pthread_mutexattr_settype(&recursiveAtt, PTHREAD_MUTEX_RECURSIVE);
///初始化鎖
pthread_mutex_init(&_recursiveLock, &recursiveAtt);
///銷毀屬性
pthread_mutexattr_destroy(&recursiveAtt);
[self recuresiveAction];
}
- (void)hanleMoney {
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf drawMoney];
}
});
}
- (void)saveMoney{
pthread_mutex_lock(&_lock);
[super saveMoney];
pthread_mutex_unlock(&_lock);
}
- (void)drawMoney {
pthread_mutex_lock(&_lock);
[super drawMoney ];
pthread_mutex_unlock(&_lock);
}
/*************************************遞歸鎖********************************************/
- (void)recuresiveAction {
static int count = 10;
pthread_mutex_lock(&_recursiveLock);
NSLog(@"count: %d", count);
if (count > 0) {
count --;
[self recuresiveAction];
}
pthread_mutex_unlock(&_recursiveLock);
}
/*************************************遞歸鎖********************************************/
- (void)dealloc {
pthread_mutex_destroy(&_lock);
pthread_mutex_destroy(&_recursiveLock);
}
@end
-
關(guān)于條件鎖的驗證代碼
首先設(shè)定以下使用場景, 兩條線程A
和B
,A
線程中執(zhí)行刪除數(shù)組元素,B
線程中執(zhí)行添加數(shù)組元素;由于不知道哪個線程會先執(zhí)行, 所以需要加鎖實現(xiàn), 只有在添加后才能執(zhí)行刪除操作;為互斥鎖添加條件可以實現(xiàn);
通過此方法可以實現(xiàn)線程依賴;
#import "ViewController4.h"
#import <pthread.h>
@interface ViewController4 ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) pthread_cond_t condition;
@end
@implementation ViewController4
- (void)viewDidLoad {
[super viewDidLoad];
/**
首先設(shè)定以下使用場景, 兩條線程 A和B, A線程中執(zhí)行刪除數(shù)組元素, B 線程中執(zhí)行添加數(shù)組元素;
由于不知道哪個線程會先執(zhí)行, 所以需要加鎖實現(xiàn), 只有在添加后才能執(zhí)行刪除操作;為互斥鎖添加條件可以實現(xiàn);
*/
///初始化鎖
pthread_mutexattr_t att;
pthread_mutexattr_init(&att);
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(&_lock, &att);
pthread_mutexattr_destroy(&att);
///初始化條件
pthread_cond_init(&_condition, NULL);
NSThread *deThread = [[NSThread alloc] initWithTarget:self selector:@selector(deleteObj) object:nil];
[deThread start];
///sleep一秒. 確保刪除元素的線程先獲得鎖;
sleep(1);
NSThread *adThread = [[NSThread alloc] initWithTarget:self selector:@selector(addObj) object:nil];
[adThread start];
}
- (void)deleteObj {
pthread_mutex_lock(&_lock);
NSLog(@"delete begin");
///添加判斷, 如果沒有數(shù)據(jù)則添加條件
if (self.dataArr.count < 1) {
/**
添加條件, 如果數(shù)組為空, 則添加等待線程休眠, 將鎖讓出; 接受到信號時會再次加鎖, 然后繼續(xù)向下執(zhí)行;
*/
pthread_cond_wait(&_condition, &_lock);
}
[self.dataArr removeLastObject];
NSLog(@"數(shù)組執(zhí)行刪除元素操作");
pthread_mutex_unlock(&_lock);
}
- (void)addObj {
pthread_mutex_lock(&_lock);
NSLog(@"add begin");
[self.dataArr addObject:@"HTI"];
///發(fā)送新號, 說明已經(jīng)添加元素了;
pthread_cond_signal(&_condition);
NSLog(@"數(shù)組執(zhí)行添加元素操作");
pthread_mutex_unlock(&_lock);
}
- (void)dealloc {
pthread_mutex_destroy(&_lock);
pthread_cond_destroy(&_condition);
}
@end
補(bǔ)充. 為什么會出現(xiàn)死鎖?
當(dāng)兩個線程中其中一個線程獲得鎖并加鎖后, 如果使用完沒有解鎖, 則另一個線程就會一直處于等待解鎖狀態(tài), 就會形成死鎖;