1、pthread_mutex简介
mutex叫做”互斥锁”,等待锁的线程会处于休眠状态。
使用时需要导入头文件:
#import <pthread/pthread.h>
有如下API:
- (void)mutexAPI {// 初始化属性pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);// 设置默认类型pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);// 初始化锁pthread_mutex_t mutex;pthread_mutex_init(&mutex, &attr);// 尝试加锁pthread_mutex_trylock(&mutex);// 加锁pthread_mutex_lock(&mutex);// 解锁pthread_mutex_unlock(&mutex);// 销毁属性pthread_mutexattr_destroy(&attr);// 销毁锁pthread_mutex_destroy(&mutex);}
创建方法:
- (void)__initMutex:(pthread_mutex_t *)mutex {// 初始化属性pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);// 设置默认类型pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);// 初始化锁pthread_mutex_init(mutex, &attr);//销毁属性pthread_mutexattr_destroy(&attr);}
加锁、解锁调用示例:
- (void)__saleTicket {pthread_mutex_lock(&_ticketLock);[super __saleTicket];pthread_mutex_unlock(&_ticketLock);}
pthread_mutex在不使用的时候需要销毁:
- (void)dealloc {// 销毁锁pthread_mutex_destroy(&_mutex);}
2、死锁问题
2.1、在加锁任务中,调用其他加锁的任务,且是同一把锁,会造成死锁
- (void)otherTest {pthread_mutex_lock(&_mutex);NSLog(@"1");[self otherTest2];pthread_mutex_unlock(&_mutex);}- (void)otherTest2 {pthread_mutex_lock(&_mutex);NSLog(@"2");pthread_mutex_unlock(&_mutex);}
2.2、加锁任务递归调用,也会产生死锁
- (void)otherTest {pthread_mutex_lock(&_mutex);NSLog(@"1");[self otherTest];pthread_mutex_unlock(&_mutex);}
3、pthread_mutex递归锁
递归锁允许同一个线程对同一把锁重复加锁,使用递归锁,就可以解决递归调用的死锁问题。
在创建mutex锁时,将mutex_attr的类型设置为递归:
- (void)__initMutex:(pthread_mutex_t *)mutex {// 初始化属性pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);// 设置递归类型pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);// 初始化锁pthread_mutex_init(mutex, &attr);//销毁属性pthread_mutexattr_destroy(&attr);}
递归调用时就不会产生死锁:
- (void)otherTest {pthread_mutex_lock(&_mutex);NSLog(@"1");[self otherTest];pthread_mutex_unlock(&_mutex);}
加锁解锁流程如下:
线程1访问:
边界条件不满足:递归前进段:——————
otherTest 加锁
otherTest 加锁
otherTest 加锁
边界条件满足:递归返回段:——————
otherTest 解锁
otherTest 解锁
otherTest 解锁
4、pthread_mutex条件
当线程间存在依赖关系时,可以使用pthread_cond来实现。
pthread_cond有如下API:
- (void)condAPI {// 初始化锁pthread_mutex_t mutex;// NULL代表使用默认属性pthread_mutex_init(&mutex, NULL);// 初始化条件pthread_cond_t conditon;pthread_cond_init(&conditon, NULL);// 等待条件(进入休眠,放开mutex锁;被唤醒后,会再次对mutex加锁)pthread_cond_wait(&conditon, &mutex);// 激活一个等待条件的线程pthread_cond_signal(&conditon);// 激活所有等待条件的线程pthread_cond_broadcast(&conditon);// 销毁锁pthread_mutex_destroy(&mutex);// 销毁条件pthread_cond_destroy(&conditon);}
例如创建两个线程和一个数组:
线程1:删除数组里的元素
线程2:向数组里添加元素
依赖关系:删除数组中的数据依赖于数组内有数据。
创建条件:
// 初始化属性pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);// 设置递归类型pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);// 初始化锁pthread_mutex_init(&_mutex, &attr);//销毁属性pthread_mutexattr_destroy(&attr);// 创建条件pthread_cond_init(&_cond, NULL);self.arr = [[NSMutableArray alloc] init];
创建线程:
- (void)otherTest {[[[NSThread alloc] initWithTarget:self selector:@selector(__remvoe) object:nil] start];sleep(0.1);[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];}
删除元素时,如果数组是空,执行pthread_cond_wait方法,开始等待条件:
- (void)__remove {pthread_mutex_lock(&_mutex);NSLog(@"线程1: 【加锁】");if (self.arr.count == 0) {NSLog(@"线程1: 开始等待(条件不成立,开始休眠,并【解锁】)");pthread_cond_wait(&_cond, &_mutex);NSLog(@"线程1: 结束等待(接收到条件成立,【加锁】并执行后续操作)");}NSLog(@"线程1: 删除元素");[self.arr removeLastObject];NSLog(@"线程1:【解锁】");pthread_mutex_unlock(&_mutex);}
添加元素后,调用pthread_cond_signal方法,通知条件成立:
- (void)__add {pthread_mutex_lock(&_mutex);NSLog(@"线程2: 【加锁】");NSLog(@"线程2: 添加元素");[self.arr addObject:@"1"];NSLog(@"线程2: 通知线程1条件成立");pthread_cond_signal(&_cond);NSLog(@"线程2: 【解锁】");pthread_mutex_unlock(&_mutex);}
也可以通过pthread_cond_broadcast(&_cond);方法,利用广播方式通知所有依赖这个条件的线程,条件成立。
打印结果:
~: 线程1: 【加锁】~: 线程1: 开始等待(条件不成立,开始休眠,并【解锁】)~: 线程2: 【加锁】~: 线程2: 添加元素~: 线程2: 通知线程1条件成立~: 线程2: 【解锁】~: 线程1: 结束等待(接收到条件成立,【加锁】并执行后续操作)~: 线程1: 删除元素~: 线程1:【解锁】
*可以看出加锁和解锁的次数是一样的,且删除元素一定在添加元素后调用。
