我們?cè)谄綍r(shí)的開發(fā)過程中可能會(huì)遇到多個(gè)線程并發(fā)同時(shí)訪問同一資源的情況,例如數(shù)據(jù)庫的存取操作,或者是文件的讀寫操作,像這種多個(gè)線程同時(shí)訪問同一資源就會(huì)容易產(chǎn)生資源競(jìng)爭(zhēng)袭厂,導(dǎo)致數(shù)據(jù)錯(cuò)亂的問題,解決這種問題我們一般會(huì)選擇使用線程同步技術(shù)球匕,也就是對(duì)這同一資源進(jìn)行加鎖解鎖操作纹磺,iOS中的鎖主要有以下幾種:
- OSSpinLock(在iOS10已經(jīng)廢棄了)
- os_unfair_lock
- pthread_mutex_t
- pthread_cond_t
- NSLock
- NSConditionLock
- @synchronized
- dispatch_semaphore_t:信號(hào)量也可以作為鎖來使用
- dispatch_sync(DISPATCH_QUEUE_SERIAL):使用串行同步隊(duì)列也可以作為鎖來使用
下面我們就逐一對(duì)上面列出的鎖進(jìn)行簡單地使用示例說明
OSSpinLock
基本用法:
@interface OSSpinLockDemo ()
@property (nonatomic, assign) OSSpinLock ossLock;
@end
@implementation OSSpinLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化自旋鎖
_ossLock = OS_SPINLOCK_INIT;
}
return self;
}
- (void)test {
// 加鎖
OSSpinLockLock(&_ossLock);
// 需要加鎖的代碼放在加鎖和解鎖語句中間,例如下面的NSLog打印
NSLog(@"加鎖代碼");
// 解鎖
OSSpinLockUnlock(&_ossLock);
}
@end
os_unfair_lock
基本用法:
@interface OSUnfairLockDemo ()
@property (nonatomic, assign) os_unfair_lock lock;
@end
@implementation OSUnfairLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化鎖
_lock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
- (void)test {
// 加鎖
os_unfair_lock_lock(&_lock);
// 需要加鎖的代碼
// 解鎖
os_unfair_lock_unlock(&_lock);
}
@end
pthread_mutex_t
基本用法:
@interface pthreadMutexLockDemo ()
@property (nonatomic, assign) pthread_mutex_t mutex;
@end
@implementation pthreadMutexLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化屬性
// pthread_mutex_t attr;
// pthread_mutexattr_init(&attr);
// PTHREAD_MUTEX_DEFAULT:是普通互斥鎖
// PTHREAD_MUTEX_RECURSIVE:遞歸鎖
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//
// // 初始化鎖
// pthread_mutex_init(&_mutex, &attr);
//
// // 銷毀屬性
// pthread_mutexattr_destroy(&attr);
// 最簡單的初始化方式是第二個(gè)參數(shù)直接填NULL即可
pthread_mutex_init(&_mutex, NULL);
}
return self;
}
- (void)test {
// 加鎖
pthread_mutex_lock(&_mutex);
// 需要加鎖的代碼
// 解鎖
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
// 銷毀鎖
pthread_mutex_destroy(&_mutex);
}
@end
pthread_cond_t
基本使用:
@interface pthreadMutexConditionLockDemo ()
@property (nonatomic, assign) pthread_mutex_t mutex;
@property (nonatomic, assign) pthread_cond_t con;
@property (nonatomic, strong) NSMutableArray *data;
@end
@implementation pthreadMutexConditionLockDemo
- (instancetype)init {
if (self = [super init]) {
// pthread_mutex_init 第二個(gè)參數(shù)可以參NULL
pthread_mutex_init(&_mutex, NULL);
pthread_cond_init(&_con, NULL);
_data = [NSMutableArray array];
}
return self;
}
- (void)test {
[[[NSThread alloc] initWithTarget:self selector:@selector(remove) object:nil] start];
sleep(2);
[[[NSThread alloc] initWithTarget:self selector:@selector(add) object:nil] start];
}
- (void)remove {
// 加鎖
pthread_mutex_lock(&_mutex);
if (!self.data.count) {
// 線程等待
pthread_cond_wait(&_con, &_mutex);
}
[self.data removeLastObject];
NSLog(@"刪除元素");
// 解鎖
pthread_mutex_unlock(&_mutex);
}
- (void)add {
// 加鎖
pthread_mutex_lock(&_mutex);
[self.data addObject:@"111"];
NSLog(@"添加元素");
// 喚醒等待的線程
pthread_cond_signal(&_con);
// 解鎖
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_con);
}
@end
NSLock
基本用法:
@interface NSLockDemo ()
@property (nonatomic, strong) NSLock *lock;
@end
@implementation NSLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化鎖
_lock = [[NSLock alloc] init];
}
return self;
}
- (void)test {
// 加鎖
[self.lock lock];
// 加鎖的代碼
// 解鎖
[self.lock unlock];
}
@end
NSConditionLock
:基本用法
@interface NSConditionLockDemo ()
@property (nonatomic, strong) NSConditionLock *conLock;
@end
@implementation NSConditionLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化鎖
_conLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (void)test {
[[[NSThread alloc] initWithTarget:self selector:@selector(remove) object:nil] start];
sleep(2);
[[[NSThread alloc] initWithTarget:self selector:@selector(add) object:nil] start];
}
- (void)remove {
// 加鎖
[self.conLock lockWhenCondition:1];
NSLog(@"刪除元素");
// 解鎖
[self.conLock unlockWithCondition:2];
}
- (void)add {
// 加鎖
[self.conLock lockWhenCondition:2];
NSLog(@"添加元素");
// 解鎖
[self.conLock unlock];
}
@end
@synchronized
基本用法:
@implementation synchronizedLockDemo
- (void)test {
// 根據(jù)傳遞的slef生成對(duì)應(yīng)的鎖亮曹,傳入不同的對(duì)象生成不同的鎖橄杨,底層是mutex 遞歸鎖
@synchronized (self) {
// 需要加鎖的代碼
}
}
@end
dispatch_semaphore_t
基本用法:
@interface semaphoneLockDemo ()
@property (nonatomic, strong) dispatch_semaphore_t semaphone;
@end
@implementation semaphoneLockDemo
- (instancetype)init {
if (self = [super init]) {
// 初始化信號(hào)量秘症,1表示同時(shí)只能有1個(gè)線程來執(zhí)行任務(wù)
_semaphone = dispatch_semaphore_create(1);
}
return self;
}
- (void)test {
for (NSInteger i = 0; i < 20; i ++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(doTask) object:nil] start];
}
}
- (void)doTask {
// 如果信號(hào)量的值>0,信號(hào)量的值就-1式矫,然后執(zhí)行后面的代碼乡摹,DISPATCH_TIME_FOREVER:一直等
// 如果信號(hào)量的值<=0,此時(shí)就進(jìn)入休眠采转,等待信息量的值變成>0
dispatch_semaphore_wait(self.semaphone, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"1212");
// 讓信號(hào)量的值+1
dispatch_semaphore_signal(self.semaphone);
}
@end
dispatch_sync(DISPATCH_QUEUE_SERIAL)
基本用法:
@interface SerialQueueLockDemo ()
@property (nonatomic, strong) dispatch_queue_t ticketQueue;
@end
@implementation SerialQueueLockDemo
- (instancetype)init {
if (self = [super init]) {
_tickeetQueue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)test {
dispatch_sync(self.tickeetQueue, ^{
// 加鎖的代碼
});
}
@end
上面我們列舉了iOS中的幾種鎖聪廉,但是有的鎖已經(jīng)廢棄了,有的鎖性能上存在些問題故慈,那么我們?cè)谄綍r(shí)的開發(fā)中會(huì)選用哪種鎖來使用比較合適尼板熊,這里根據(jù)大量的三方框架和蘋果底層框架中使用到的鎖的情況來看,NSLock
察绷,pthread_mutex
干签,dispatch_semaphore
是出現(xiàn)頻次最多的
上面我們介紹的這些鎖對(duì)于多個(gè)異步線程同時(shí)訪問同一資源時(shí),使用這些鎖來進(jìn)行線程同步確實(shí)是可以解決問題拆撼,但是如果我們是對(duì)文件進(jìn)行讀寫操作容劳,這時(shí)上面的鎖就不太適合使用了,因?yàn)槲募x寫操作的特點(diǎn)是可以同時(shí)讀情萤,但是不能同時(shí)寫,也不能在讀的時(shí)候進(jìn)行寫操作摹恨,也不能在寫的時(shí)候進(jìn)行讀操作筋岛,所以就有了下面的兩種專門處理文件讀寫操作的鎖:
- pthread_rwlock_t
- dispatch_barrier_async:異步柵欄也可以解決文件讀寫操作
pthread_rwlock_t
基本用法:
@interface pthread_rwlockDemo ()
@property (nonatomic, assign) pthread_rwlock_t rwlock;
@end
@implementation pthread_rwlockDemo
- (void)test {
// 初始化鎖
pthread_rwlock_init(&_rwlock, NULL);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self read];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self write];
});
}
}
- (void)read {
// 讀加鎖
pthread_rwlock_rdlock(&_rwlock);
sleep(1);
NSLog(@"開始讀");
// 解鎖
pthread_rwlock_unlock(&_rwlock);
}
- (void)write {
// 寫加鎖
pthread_rwlock_wrlock(&_rwlock);
sleep(1);
NSLog(@"開始寫");
// 解鎖
pthread_rwlock_unlock(&_rwlock);
}
- (void)dealloc {
// 銷毀鎖
pthread_rwlock_destroy(&_rwlock);
}
@end
dispatch_barrier_async
:基本用法:
@interface dispatch_barrier_asyncDemo ()
@property (nonatomic, strong) dispatch_queue_t queue;
@end
@implementation dispatch_barrier_asyncDemo
- (void)test {
// 創(chuàng)建一個(gè)并發(fā)隊(duì)列,注意:這里使用dispatch_barrier_async只能使用`dispatch_queue_create`創(chuàng)建一個(gè)并發(fā)隊(duì)列晒哄,不能使用全局并發(fā)隊(duì)列
self.queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
[self read];
[self read];
[self read];
[self read];
[self write];
}
}
- (void)read {
dispatch_async(self.queue, ^{
sleep(1);
NSLog(@"開始讀");
});
}
- (void)write {
dispatch_barrier_async(self.queue, ^{
// 開始加鎖
sleep(1);
NSLog(@"開始寫");
});
}
@end
講解示例Demo地址:https://github.com/guangqiang-liu/09.1-ThreadLock
更多文章
- ReactNative開源項(xiàng)目OneM(1200+star):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
- iOS組件化開發(fā)實(shí)戰(zhàn)項(xiàng)目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:歡迎小伙伴們 star
- 簡書主頁:包含多篇iOS和RN開發(fā)相關(guān)的技術(shù)文章http://www.reibang.com/u/023338566ca5 歡迎小伙伴們:多多關(guān)注睁宰,點(diǎn)贊
- ReactNative QQ技術(shù)交流群(2000人):620792950 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
- iOS QQ技術(shù)交流群:678441305 歡迎小伙伴進(jìn)群交流學(xué)習(xí)