IOS 关于锁
作者:互联网
前言
为什么需要锁:
多条线程存在同时操作(删、查、读、写)同一个文件or对象or变量。如果不是同时或者不是同一个那就不用加锁了。关键变量:必须是“同时”,“同一事物”
自旋锁OSSpinLock
- 个人理解:就是一直等,忙等,一直while 到锁被解开,自旋锁不会让等待的线程进入休眠状态
- 已经废弃,容易出现优先级反转
- 关于优先级反转:现在有2个高优先级线程A,一个低优先级线程B
- 情况1:A先获得锁,执行A线程的任务,然后B获取锁,执行B的任务。正常
- 情况2:B先获得锁,因为B是低优先级线程,这个时候系统会给A分配相对多的系统资源,B能获得少量资源,B的任务先执行,A后执行,正常
- 情况3:B先获取锁,因为B是低优先级线程,这个时候系统会给A分配相对多的系统资源,没有分配给B资源。这种情况下,B获取了锁,但是没有系统资源,就会一直等待系统资源,造成死锁。
/// 自旋锁 需要导入头文件:#import <libkern/OSAtomic.h>
- (void)testSpinLock {
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"1准备上锁");
OSSpinLockLock(&oslock);
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
OSSpinLockUnlock(&oslock);
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"2准备上锁");
OSSpinLockLock(&oslock);
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
OSSpinLockUnlock(&oslock);
NSLog(@"2解锁");
});
}
不公平锁os_unfair_lock
- iOS 10 后用来代替spinLock
- 底层锁,尝试获取锁不会忙等,解锁时由内核唤醒
/// 不公平锁:#import <os/lock.h>
- (void)testUnfailLock {
static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1准备上锁");
os_unfair_lock_lock(&lock);
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
os_unfair_lock_unlock(&lock);
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2准备上锁");
os_unfair_lock_lock(&lock);
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
os_unfair_lock_unlock(&lock);
NSLog(@"2解锁");
});
}
互斥锁
- 可移植操作系统接口(Portable Operating System Interface,缩写为 POSIX)
- 这个是iOS 底层锁,可以实现多种上层锁:递归锁,条件锁等等
pthread_mutex_t
/// 互斥锁 #import <pthread.h>
- (void)testMutexLock {
static pthread_mutex_t mutex_t;
pthread_mutex_init(&mutex_t,NULL);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1准备上锁");
pthread_mutex_lock(&mutex_t);
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
pthread_mutex_unlock(&mutex_t);
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2准备上锁");
pthread_mutex_lock(&mutex_t);
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
pthread_mutex_unlock(&mutex_t);
NSLog(@"2解锁");
});
// pthread_mutex_destroy(&recursiveMutex)
}
NSLock
- NSLock 是对pthread_mutex 的封装
- NSLock 不是递归锁,不能在同一线程多次加锁,会造成死锁
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
{
DESTROY(self);
}
}
return self;
}
- (BOOL) lockBeforeDate: (NSDate*)limit
{
do{
int err = pthread_mutex_trylock(&_mutex);
if (0 == err)
{
CHK(Hold)
return YES;
}
if (EDEADLK == err)
{
(*_NSLock_error_handler)(self, _cmd, NO, @"deadlock");
}
sched_yield();
} while ([limit timeIntervalSinceNow] > 0);
return NO;
}
- (void)testNSLock {
NSLock *lock = [NSLock new];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
[lock lock];
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
[lock unlock];
NSLog(@"1解锁");
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
// 尝试上锁,获取资源,如果获取不超过,释放自由,在给定的时间内,一直轮询获取锁,
BOOL ret = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
if (ret) {
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
[lock unlock];
}else{
NSLog(@"失败");
}
});
}
递归锁
- 同一个线程,多次加锁
MutexRecursive
- 使用 pthread_cond_wait or pthread_cond_timedwait 需要先上锁
- pthread_cond_t 与 pthread_mutex_t 应该一一对应,不可多个pthread_mutex_t 使用同一个pthread_cond_t
- wait 时线程处于阻塞态,并被挂起,不占用CPU,知道收到信号被唤醒,重新上锁
/// 递归锁 MutexRecursive
- (void)testMutexRecursive {
static pthread_mutex_t mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_t, &attr);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^OneBlock)(int);
OneBlock = ^(int value) {
NSLog(@"%d准备上锁",value);
pthread_mutex_lock(&mutex_t);
int time = arc4random() %5;
NSLog(@"%d线程:%d",value,time);
if(value > 0){
OneBlock(value - 1);
}
sleep(time);
pthread_mutex_unlock(&mutex_t);
NSLog(@"%d解锁",value);
};
OneBlock(5);
NSLog(@"执行完成");
});
// pthread_mutex_destroy(&recursiveMutex)
}
NSRecursiveLock
- NSRecursiveLock 也是 对pthread_mutex 的封装
- (void)testNSRecursiveLock {
NSRecursiveLock *recursiveLock = [NSRecursiveLock new];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^OneBlock)(int);
OneBlock = ^(int value) {
NSLog(@"%d准备上锁",value);
[recursiveLock lock];
int time = arc4random() %5;
NSLog(@"%d线程:%d",value,time);
if(value > 0){
OneBlock(value - 1);
}
sleep(time);
[recursiveLock unlock];
NSLog(@"%d解锁",value);
};
OneBlock(5);
NSLog(@"执行完成");
});
}
条件锁
pthread_cond
- (void)testMutexCond {
static pthread_mutex_t mutex_t;
static pthread_cond_t cond_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_t, &attr);
pthread_cond_init(&cond_t,NULL);
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
pthread_mutex_lock(&mutex_t);
pthread_cond_wait(&cond_t, &mutex_t);
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
pthread_mutex_unlock(&mutex_t);
NSLog(@"1解锁");
});
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2准备上锁");
pthread_mutex_lock(&mutex_t);
struct timespec outtime;
outtime.tv_sec = time(NULL) + 5;
outtime.tv_nsec = 0;
pthread_cond_timedwait(&cond_t, &mutex_t, &outtime);
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
pthread_mutex_unlock(&mutex_t);
NSLog(@"2解锁");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"发送了一个信号");
pthread_cond_signal(&cond_t);
// 广播一个信号
// pthread_cond_broadcast(&cond_t);
});
// pthread_mutex_destroy(&mutex_t);
// pthread_cond_destroy(&cond);
// pthread_mutexattr_destroy(&attr);
}
NSCondition
- NSCondition 是对 pthread_mutex_t 和 pthread_cond_t 的封装 (GNUstep 的实现方式)
- 条件锁,通过信号来控制
lock
上锁unlock
解锁wait
/waitUntilDate
等待一个信号,或者超时,等待的时候线程休眠,会暂时解锁,这个时候其他线程可以上锁,等待结束后,重新自动上锁,signal
发送一个信号,如果有多个线程中都在等待,只有一个线程会接收到broadcast
广播一个信号,如果有多个线程中都在等待,每一个线程会接收到。注意,这里是当上一个线程解锁后,下一个线程才回收到这个广播信号
- (void)testCondition {
NSCondition *condition = [NSCondition new];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
[condition lock];
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
[condition unlock];
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2准备上锁");
[condition lock];
NSLog(@"2成功上锁,进入等待");
if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:20]]) {// 等一个信号,或者超时, 如果这个线程还使用了lock 需要放在lock 之后
NSLog(@"2收到了信号");
}else {
NSLog(@"2超时");
}
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
[condition unlock];
NSLog(@"2解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"3准备上锁");
[condition lock];
NSLog(@"3成功上锁,进入等待");
[condition wait];// 不设置超时 一直等待知道收到信号
int time = arc4random() %5;
NSLog(@"3线程:%d",time);
sleep(time);
[condition unlock];
NSLog(@"3解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"4准备上锁");
[condition lock];
NSLog(@"4成功上锁");
int time = arc4random() %5;
NSLog(@"4线程:%d",time);
sleep(time);
[condition unlock];
NSLog(@"4解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"5准备上锁");
[condition lock];
NSLog(@"5成功上锁,进入等待");
if ([condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:50]]) {// 等一个信号,或者超时, 如果这个线程还使用了lock 需要放在lock 之后
NSLog(@"5收到了信号");
}else {
NSLog(@"5超时");
}
int time = arc4random() %5;
NSLog(@"5线程:%d",time);
sleep(time);
[condition unlock];
NSLog(@"5解锁");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"发送了一个信号");
// [condition signal];
// 或者广播一个信号,
[condition broadcast];
});
}
NSConditionLock
- NSConditionLock 是对 NSCondition 的再一次封装
- Condition 等于设置的数字时,才可以加锁成功
- 通过数字来实现
- 可以实现任务之间的相互依赖
- (void)testNSConditionLock {
NSConditionLock *oneLock = [[NSConditionLock alloc] initWithCondition:0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
[oneLock lock];
NSLog(@"1成功上锁");
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
[oneLock unlock];
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2准备上锁");
[oneLock lockWhenCondition:0];
NSLog(@"2成功上锁");
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
[oneLock unlockWithCondition:3];
NSLog(@"2解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"3准备上锁");
[oneLock lockWhenCondition:3];
NSLog(@"3成功上锁");
int time = arc4random() %5;
NSLog(@"3线程:%d",time);
sleep(time);
[oneLock unlockWithCondition:4];
NSLog(@"3解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"4准备上锁");
[oneLock lockWhenCondition:4];
NSLog(@"4成功上锁");
int time = arc4random() %5;
NSLog(@"4线程:%d",time);
sleep(time);
[oneLock unlockWithCondition:5];
NSLog(@"4解锁");
});
}
读写锁
- (void)testrwLock {
static pthread_rwlock_t rwlock_t;
pthread_rwlock_init(&rwlock_t, nil);
for (int i = 0; i< 10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%dread ready",i);
pthread_rwlock_rdlock(&rwlock_t);
int time = arc4random() %5;
NSLog(@"%dreading:%d",i,time);
sleep(time);
NSLog(@"%dreaded",i);
pthread_rwlock_unlock(&rwlock_t);
});
}
for (int i = 10; i< 20; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%dwrite ready",i);
pthread_rwlock_wrlock(&rwlock_t);
int time = arc4random() %5;
NSLog(@"%dwriting:%d",i,time);
sleep(time);
NSLog(@"%dwriteded",i);
pthread_rwlock_unlock(&rwlock_t);
});
}
for (int i = 20; i< 30; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%dread ready",i);
pthread_rwlock_rdlock(&rwlock_t);
int time = arc4random() %5;
NSLog(@"%dreading:%d",i,time);
sleep(time);
NSLog(@"%dreaded",i);
pthread_rwlock_unlock(&rwlock_t);
});
}
}
synchronized
- 一个简单的,性能偏低的,内部实现递归的锁
- (void)testsynchronized {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"1准备上锁");
@synchronized (self) {
NSLog(@"1成功上锁");
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
}
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2准备上锁");
@synchronized (self) {
NSLog(@"2成功上锁");
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
}
NSLog(@"2解锁");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"3准备上锁");
@synchronized (self) {
NSLog(@"3成功上锁");
int time = arc4random() %5;
NSLog(@"3线程:%d",time);
sleep(time);
}
NSLog(@"3解锁");
});
}
信号量
- (void)testSemphore{
dispatch_semaphore_t semp = dispatch_semaphore_create(1); //传入值必须 >=0, 若传入为0则阻塞线程并等待timeout,时间到后会执行其后的语句
dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 5.0f * NSEC_PER_SEC);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1准备上锁");
dispatch_semaphore_wait(semp, overTime);// 如果signal > 0 则-1 继续向下执行,如果signal == 0 则等待
int time = arc4random() %5;
NSLog(@"1线程:%d",time);
sleep(time);
dispatch_semaphore_signal(semp); // singal +1
NSLog(@"1解锁");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2准备上锁");
dispatch_semaphore_wait(semp, overTime);
int time = arc4random() %5;
NSLog(@"2线程:%d",time);
sleep(time);
dispatch_semaphore_signal(semp);
NSLog(@"2解锁");
});
}
总结:
- 什么时候使用锁?
- 当多个线程同时访问同一块资源的时候,需要使用锁
- 什么情况下使用什么样的锁?
- 自旋锁:循环等待锁,一直占用着CPU资源,如果临界资源小,处理资源耗时少,则可以使用,如:从内存中直接获取
- 不公平锁:取代自旋锁的,当你在iOS10 后,想用自旋锁的时候,就用这个
- 互斥锁:线程调度消耗CPU资源,如果资源大,处理资源耗时长,则可以使用,如:从网络中获取资源,大量计算等
- 读写锁:当多个线程对同一个资源存在大量读取操作,少量写操作时使用,
- synchronized: 当你懒的时候使用,虽然性能不怎么好,但是简单
标签:IOS,pthread,NSLog,dispatch,线程,关于,time,mutex 来源: https://blog.csdn.net/wutao63/article/details/122428961