大家好,我是OB!今天来聊聊锁!
Lock 锁:防⽌止多个线程可能会访问同⼀块资源引发的问题,加锁可以保证在多线程环境下的数据访问安全,但同时牺牲了访问效率。
先看看各所锁的性能测试:
性能测试,简单的加解锁,测试结果仅供参考,每次测试结果均不同,但大体一样。测试代码在文章末尾。
Test_Lock[27167:14791830] 0.000257 ->OSSpinLock
Test_Lock[27167:14791830] 0.000399 ->testDispatch_semaphore
Test_Lock[27167:14791830] 0.000411 ->os_unfair_lock_lock
Test_Lock[27167:14791830] 0.000448 ->test_pthread_mutex_lock
Test_Lock[27167:14791830] 0.000504 ->test_pthread_rwlock
Test_Lock[27167:14791830] 0.000789 ->testNSLock
Test_Lock[27167:14791830] 0.000848 ->testRecursiveLock
Test_Lock[27167:14791830] 0.001357 ->testConditionlock
Test_Lock[27167:14791830] 0.001043 ->testSynchronized
可以看出:OSSpinLock
效率最高(自旋锁,由于不安全不参与比较,你开挂我就踢你[狗头保命]),
然后前三是:Dispatch_semaphore
,os_unfair_lock_lock
,pthread_mutex_lock
后面三个分别是:NSRecursiveLock
,NSConditionlock
,@synchronized
是用于多线程同步的种锁,线程反复检查锁变量是否可用,处于忙等状态(一直进行do while循环)
忙等:等待锁的线程会处于忙等(busy-wait)状态,一直占⽤用着CPU资源;
High-level lock,⾼级锁,等不不到锁时忙等,不会休眠
引入#import
static OSSpinLock spinLock = OS_SPINLOCK_INIT;
OSSpinLockTry( volatile OSSpinLock *__lock );
OSSpinLockLock( volatile OSSpinLock *__lock );
OSSpinLockUnlock( volatile OSSpinLock *__lock );
注意:
spinLock
变量使用static
或者__block
修饰,不然结果无效
目前已经不再安全,可能会出现优先级反转问题 ;
⾃旋锁优先级反转:有两个线程,thread1(⾼优先级)、thread2(低优先级),thread2先进⾏加锁操作,CPU切换调度,thread1进⼊,发现已经被锁,进⼊自旋状态。由于thread1优先级⽐ thread2⾼出很多,CPU接下来可能⼀直调度thread1,处于自旋状态,相当于⼀直执行不到 thread2的解锁操作,造成优先级反转现象
所以OSSpinLock
在 iOS 10 后被 os_unfair_lock_lock
代替,因为前者会出现优先级反转问题(本质已经变成了了互斥锁)
使用和代替它的os_unfair_lock_lock
使用一样。
os_unfair_lock_trylock(unfairLock);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
使用如下:
void test_os_unfair_lock_lock() {
__block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
os_unfair_lock_lock(&unfairLock);
NSLog(@"%@ -- start-",[NSThread currentThread]);
sleep(3);
NSLog(@"%@ -- end",[NSThread currentThread]);
os_unfair_lock_unlock(&unfairLock);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
os_unfair_lock_lock(&unfairLock);
NSLog(@"%@ -- start-",[NSThread currentThread]);
sleep(1);
NSLog(@"%@ -- end",[NSThread currentThread]);
os_unfair_lock_unlock(&unfairLock);
});
}
打印:
Test_Lock[13172:12405001] <NSThread: 0x10056e640>{number = 2, name = (null)} -- start-
Test_Lock[13172:12405001] <NSThread: 0x10056e640>{number = 2, name = (null)} -- end
Test_Lock[13172:12405002] <NSThread: 0x100703850>{number = 3, name = (null)} -- start-
Test_Lock[13172:12405002] <NSThread: 0x100703850>{number = 3, name = (null)} -- end
常用函数API
dispatch_semaphore_t dispatch_semaphore_create(long value)
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
其中出现wait,value将减一,出现signal,value加一,减到0或者负数将会阻塞,创建时传入0,
dispatch_semaphore_create(0)
,如果先遇到wait,那么线程直接阻塞。
创建信号量的时候可以设置最大并发数dispatch_semaphore_create(long value)
中的 value
就是线程最大并发数。和NSOperationQueue
中的maxConcurrentOperationCount
作用相似
void testDispatch_semaphore () {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t timeout = DISPATCH_TIME_FOREVER;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, timeout);
NSLog(@"%@ -- start-",[NSThread currentThread]);
sleep(1);
NSLog(@"%@ -- end",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, timeout);
NSLog(@"%@ -- start-",[NSThread currentThread]);
sleep(1);
NSLog(@"%@ -- end",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}
打印如下
Test_Lock[14273:12433586] <NSThread: 0x10055ce60>{number = 2, name = (null)} -- start-
Test_Lock[14273:12433586] <NSThread: 0x10055ce60>{number = 2, name = (null)} -- end
Test_Lock[14273:12433588] <NSThread: 0x102001560>{number = 3, name = (null)} -- start-
Test_Lock[14273:12433588] <NSThread: 0x102001560>{number = 3, name = (null)} -- end
互斥锁,当一个线程拥有锁时,其他线程等待,只有当锁释放时,其他线程才能拥有锁,往下执行。
- (void)lock;
- (void)unlock;
使用也简单
void test(NSLock *lock);
void testDispatch_semaphore () {
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
test(lock);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
test(lock);
});
}
static int _tickets = 3;
void test(NSLock *lock) {
while (1) {
//@synchronized (token) {
[lock lock];
sleep(1);
if (_tickets > 0) {
_tickets--;
NSLog(@"剩余票数= %d, Thread:%@",_tickets,[NSThread currentThread]);
} else {
NSLog(@"票卖完了 Thread:%@",[NSThread currentThread]);
[lock unlock]; //要释放锁
break;
}
[lock unlock];
//}
}
}
可将while
循环中锁换成@synchronized
打印如下:
Test_Lock[14927:12451072] 剩余票数= 2, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 剩余票数= 1, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 剩余票数= 0, Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451072] 票卖完了 Thread:<NSThread: 0x10075fec0>{number = 2, name = (null)}
Test_Lock[14927:12451071] 票卖完了 Thread:<NSThread: 0x10075ff00>{number = 3, name = (null)}
只有 condition
参数与初始化时候的 condition
相等,conditionlock
才能正确进行加锁操作。
unlockWithCondition:
是解锁之后,修改 condition
的值,并不是当 condition
符合条件时才解锁
void testConditionlock() {
NSConditionLock * conditionlock = [[NSConditionLock alloc] initWithCondition:0];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionlock lock];
NSLog(@"1 lock success");
sleep(1);
[conditionlock unlockWithCondition:9];
NSLog(@"1 unlock success");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionlock lockWhenCondition:9];
sleep(1);
NSLog(@"2 lock success");
[conditionlock unlock];
NSLog(@"2 unlock success");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(2);
if ([conditionlock tryLockWhenCondition:0]) {
NSLog(@"3 lock success");
sleep(2);
[conditionlock unlockWithCondition:2];
NSLog(@"3 unlock success");
} else {
NSLog(@"3 lock fail");
}
});
}
打印如下:
Test_Lock[15144:12458143] 1 lock success
Test_Lock[15144:12458143] 1 unlock success
Test_Lock[15144:12458148] 3 lock fail
Test_Lock[15144:12458147] 2 lock success
Test_Lock[15144:12458147] 2 unlock success
static NSRecursiveLock *recursiceLock = nil;
void testRecursiveLock(int count) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
recursiceLock = [[NSRecursiveLock alloc] init];
});
if (count <= 0) {
return;
}
NSLog(@"当前数量:%d",count);
testRecursiveLock(--count);
}
在同⼀一线程上该锁是可重⼊入的,对于不不同线程则相当于 普通的互斥锁
读写锁,在对文件进行操作的时候,写操作是互斥的,只能一个线程写,写完下个线程再写,写操作的时候,所有读的线程全部阻塞,只有等写操作完成了,才能读。
读操作时,写线程也是要阻塞,等读完释放锁才能写,但是读是可以多个线程一起读的
// 初始化
pthread_rwlock_t lock;
pthread_rwlock_init(&lock, NULL);
// 读模式
pthread_rwlock_wrlock(&lock);
// 写模式
pthread_rwlock_rdlock(&lock);
// 读模式或者写模式的解锁
pthread_rwlock_unlock(&lock);
使用如下
#import
pthread_rwlock_t lock;
void readBook () {
pthread_rwlock_rdlock(&lock);
NSLog(@"start read ---- %@",[NSThread currentThread]);
sleep(3);
NSLog(@"end read ---- %@",[NSThread currentThread]);
pthread_rwlock_unlock(&lock);
}
void writeBook () {
pthread_rwlock_wrlock(&lock);
NSLog(@"start write ---- %@",[NSThread currentThread]);
sleep(4);
NSLog(@"end write ---- %@",[NSThread currentThread]);
pthread_rwlock_unlock(&lock);
}
void test_pthread_rwlock () {
pthread_rwlock_init(&lock, NULL);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
writeBook();
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
writeBook();
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
readBook();
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
readBook();
});
}
打印如下:
Test_Lock[24941:14660535] start write ---- <NSThread: 0x100504c70>{number = 2, name = (null)}
Test_Lock[24941:14660535] end write ---- <NSThread: 0x100504c70>{number = 2, name = (null)}
Test_Lock[24941:14660537] start write ---- <NSThread: 0x10204c130>{number = 3, name = (null)}
Test_Lock[24941:14660537] end write ---- <NSThread: 0x10204c130>{number = 3, name = (null)}
Test_Lock[24941:14660538] start read ---- <NSThread: 0x10204c220>{number = 4, name = (null)}
Test_Lock[24941:14660539] start read ---- <NSThread: 0x100609560>{number = 5, name = (null)}
Test_Lock[24941:14660538] end read ---- <NSThread: 0x10204c220>{number = 4, name = (null)}
Test_Lock[24941:14660539] end read ---- <NSThread: 0x100609560>{number = 5, name = (null)}
他的API
int pthread_mutex_init(pthread_mutex_t * __restrict,
const pthread_mutexattr_t * _Nullable __restrict);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
使用如下
变量
pthread_mutex_t
必须是static
,__block
或者全局变量
中的一种,不然锁会无效,这里涉及到block
的变量捕获原则,必须捕获指针或者是不捕获。局部变量是值捕获,不可以的。
#import
void test_pthread_mutex_lock() {
static pthread_mutex_t mutex_lock;
pthread_mutex_init(&mutex_lock, NULL);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
pthread_mutex_lock(&mutex_lock);
NSLog(@"start ---- %@",[NSThread currentThread]);
sleep(4);
NSLog(@"end ---- %@",[NSThread currentThread]);
pthread_mutex_unlock(&mutex_lock);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
pthread_mutex_lock(&mutex_lock);
NSLog(@"start ---- %@",[NSThread currentThread]);
sleep(2);
NSLog(@"end ---- %@",[NSThread currentThread]);
pthread_mutex_unlock(&mutex_lock);
});
}
打印
Test_Lock[26628:14770428] start ---- <NSThread: 0x102007890>{number = 2, name = (null)}
Test_Lock[26628:14770428] end ---- <NSThread: 0x102007890>{number = 2, name = (null)}
Test_Lock[26628:14770429] start ---- <NSThread: 0x10066b190>{number = 3, name = (null)}
Test_Lock[26628:14770429] end ---- <NSThread: 0x10066b190>{number = 3, name = (null)}
测试代码:
#import
#import
static OSSpinLock spinLock;
void testOSSpinLock() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
spinLock = OS_SPINLOCK_INIT;
});
OSSpinLockLock(&spinLock);
OSSpinLockUnlock(&spinLock);
}
static os_unfair_lock unfairLock;
void test_os_unfair_lock_lock() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
unfairLock = OS_UNFAIR_LOCK_INIT;
});
os_unfair_lock_lock(&unfairLock);
os_unfair_lock_unlock(&unfairLock);
}
static dispatch_semaphore_t semaphoreLock;
void testDispatch_semaphore () {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
semaphoreLock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphoreLock);
}
static NSLock *lock = nil;
void testNSLock () {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = [[NSLock alloc] init];
});
[lock lock];
[lock unlock];
}
static NSConditionLock * conditionlock = nil;
void testConditionlock() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
conditionlock = [[NSConditionLock alloc] initWithCondition:0];
});
[conditionlock lock];
[conditionlock unlock];
}
static NSRecursiveLock *recursiceLock = nil;
void testRecursiveLock() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
recursiceLock = [[NSRecursiveLock alloc] init];
});
[recursiceLock lock];
[recursiceLock unlock];
}
static NSString *token =nil;
void testSynchronized () {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
token = @"key_token";
});
@synchronized (token) {
}
}
#import
static pthread_rwlock_t wrlock;
void test_pthread_rwlock () {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pthread_rwlock_init(&wrlock, NULL);
});
pthread_rwlock_wrlock(&wrlock);
pthread_rwlock_unlock(&wrlock);
}
static pthread_mutex_t mutex_lock;
void test_pthread_mutex_lock() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pthread_mutex_init(&mutex_lock, NULL);
});
pthread_mutex_lock(&mutex_lock);
pthread_mutex_unlock(&mutex_lock);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// NSDate *date = [[NSDate alloc]init];
NSTimeInterval ti = [[NSDate new] timeIntervalSince1970];
NSTimeInterval cur_ti = 0;
int count = 10000;
for (int i = 0; i <count; i++) {
testOSSpinLock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->OSSpinLock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
testDispatch_semaphore();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->testDispatch_semaphore",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
test_os_unfair_lock_lock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->os_unfair_lock_lock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
test_pthread_mutex_lock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->test_pthread_mutex_lock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
test_pthread_rwlock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->test_pthread_rwlock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
testNSLock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->testNSLock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
testRecursiveLock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->testRecursiveLock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
testConditionlock();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->testConditionlock",cur_ti);
ti = [[NSDate new] timeIntervalSince1970];
for (int i = 0; i <count; i++) {
testSynchronized();
}
cur_ti = [[NSDate new] timeIntervalSince1970] - ti;
NSLog(@"%f ->testSynchronized",cur_ti);
}
return 0;
}