多线程六——加锁方案三 :pthread_mutex

一、定义

  • mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
  • 需要导入头文件#import <pthread.h>
    在这里插入图片描述

解释:

pthread 开头的都是跨平台的,一般是通用的
* 可以在 windows, Linux ,mac 等
* 如果在 iOS 上会用这个锁,那么在其他平台也会使用这把锁。
* 它是互斥锁:mutex

现在有两种锁:
* 互斥锁
* 自旋锁


二、代码演示,静态初始化

// 初始化锁 
- (instancetype)init
{
    if (self = [super init]) {
    	/// 初始化锁 
    	/// 但是当 使用属性赋值时,会报错。
    	/// 是静态初始化,在定义变量的同时,赋值
        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    }
    return self;
}

  • 创建属性 @property(nonatomic,assign) pthread_mutex_t mutex;,赋值 属性 self.mutex = PTHREAD_MUTEX_INITIALIZER; 会报错。
  • 点进 PTHREAD_MUTEX_INITIALIZER 查看 #define PTHREAD_MUTEX_INITIALIZER {_PTHREAD_MUTEX_SIG_init, {0}}
  • 试试看可不可以 把结构体给 属性赋值 ?self.mutex = {_PTHREAD_MUTEX_SIG_init, {0}}; 不可以。会报错。
  • self.mutex = *** ; 其实就是self 调用 setMutex [self setMutex:***];
  • 定义结构体的时候必须要在定义的同时,赋值

结构体赋值
正确写法:

struct Date {
            int year;
            int month;
 };
// 2011 给 year,10给month
struct Date date = {2011,10};

不能写成

struct Date date;
date = {2011,10};

这是结构体语法规范


三、初始化

  • 初始化属性
/// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
  • 初始化锁
/// 初始化锁
 pthread_mutex_init(&_mutex, &attr);
  • 设置锁的类型
  • pthread_mutexattr_settype(&attr, 写下面的 0,1,2,)
/*
 * Mutex type attributes
 */
#define PTHREAD_MUTEX_NORMAL		0
#define PTHREAD_MUTEX_ERRORCHECK	1
#define PTHREAD_MUTEX_RECURSIVE		2
#define PTHREAD_MUTEX_DEFAULT		PTHREAD_MUTEX_NORMAL
  • PTHREAD_MUTEX_NORMALPTHREAD_MUTEX_DEFAULT 都是普通锁

  • PTHREAD_MUTEX_ERRORCHECK 用来检查错误的

  • PTHREAD_MUTEX_RECURSIVE 递归锁

  • pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);

  • 销毁属性

/// 销毁属性
pthread_mutexattr_destroy(&attr);

以上代码,代表初始化完一把 锁
全部代码为:

- (instancetype)init
{
    if (self = [super init]) {
        /// 初始化属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT)
        /// 初始化锁
        pthread_mutex_init(&_mutex, &attr);
        
        /// 销毁属性
        pthread_mutexattr_destroy(&attr);
    }
    return self;
}

剩下的代码 :
在这里插入图片描述


四、细节
细节一:init 时传NULL

  • 当写 初始化锁的时候,可以不用写初始化属性,直接传入NULL 。 当为空时,相当于传的是 PTHREAD_MUTEX_DEFAULT
 /// 初始化锁
 pthread_mutex_init(&_mutex, NULL);

细节二:不用的时候,要销毁

- (void)dealloc {
    pthread_mutex_destroy(&_moneyMutex);
    pthread_mutex_destroy(&_ticketMutex);
}

五、例子2
一个方法里面调用另一个方法。两个方法都加锁
代码:

- (void)otherTest {
    pthread_mutex_lock(&_mutex);
    NSLog(@"%s", __func__);
    [self otherTest2];
    pthread_mutex_unlock(&_mutex);
}

- (void)otherTest2 {
    pthread_mutex_lock(&_mutex);
    NSLog(@"%s", __func__);
    pthread_mutex_unlock(&_mutex);
}

执行结果: 死锁
解决办法:换锁,两个方法不用一把锁。

没有锁的时候,打印是正常的:

- (void)otherTest {
    NSLog(@"%s", __func__);
    [self otherTest2];
}

- (void)otherTest2 {
    NSLog(@"%s", __func__);
}

打印结果:
在这里插入图片描述

如果是递归,也会出现死锁的情况

- (void)otherTest {
    pthread_mutex_lock(&_mutex);
    NSLog(@"%s", __func__);
    // 自己调用自己
    [self otherTest];
    pthread_mutex_unlock(&_mutex);
}

解决办法: 使用递归锁

pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

改完以后,会一直循环。
修改代码:
在这里插入图片描述


递归锁:允许同一个线程对一把锁进行重复加锁。

如果是线程1 、线程2这样,就不是同一个线程。
它会自动判断是否是同一个线程过来加锁。



六、条件

往数组中添加、删除 元素。应该怎么做?

  • 添加元素和删除元素的时候,加同一把锁。

  • 在 otherTest 方法中,调用这两个方法。先条用删除元素,再调用添加元素。

  • 假如:删除元素和添加元素 是在不同线程做的。

  • 想要做的效果是:数组中有元素的时候在删除,没有元素就不删除。

  • 想要做的效果如下:
    在这里插入图片描述

第一步:创建一个条件的属性
在这里插入图片描述

第二步:初始化条件
在这里插入图片描述

第三步:当不需要条件后,要销毁
在这里插入图片描述

第四步: 使用条件
在这里插入图片描述

解释:

  1. 如果有一条线程调用 remove 方法,另一条线程调用 add 方法。当A线程调用 remove 方法时,会对mutex 锁进行加锁。当B线程 调用 add 方法时,要对 mutex 锁进行加锁。由于线程A先进行的加锁,线程B就只能等待。
  2. remove 方法中有 一个判断,如果数组元素为0,那么锁进行等待。这个锁在睡觉的时候,会被放开。pthread_cond_wait(&_cond,&_mutex);这句代码在睡觉的时候,会把锁放开。
  3. 这个时候,线程B中的add方法,就可以拿到mutex这把锁。
  4. 一旦数组中有元素了,那么应该告诉 remove 方法,让他删除。
  5. 在 add 方法中 写 pthread_cond_signal(&_cond); 这句代码会唤醒pthread_cond_wait(&_cond,&_mutex);这句代码。
    在这里插入图片描述
  6. 唤醒后,pthread_cond_wait(&_cond,&_mutex); 代码会给 _mutex 加锁。然后代码执行到remove 方法中的 [self.data removeLastObject]; ,然后就会解除 remove 方法中的 锁。
  7. 也就是说 pthread_cond_wait(&_cond,&_mutex); 这个代码,在等待的时候,会 解锁。然后睡觉。当唤醒的时候。会再次加锁。
  8. 使用场景:线程1 依赖线程2. 一个线程依赖另一个线程。
  9. 生产者 - 消费者模式
  10. 一边生产,一边消费。没有生产就没有消费。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值