目录
2.pthread_cond_wait的内部是针对互斥锁做了上什么操作?先释放互斥锁还是先将线程放入到PCB等待队列?
一、同步
1.有了互斥还为什么要有同步?
多个线程保证了互斥, 也就是保证了线程能够合理的访问临界资源了。但并不是说, 各个线程在访问临界资源的时候都是合理的。同步是为了保证多个线程对临界资源的访问的合理性。这个合理性建立在多个线程保证互斥的情况下
如果只保证互斥,有可能出现“线程饥饿”问题,那么多线程不久变成了单线程吗?
比如现实生活种的问题,去面馆吃面
我们在吃面的生活,不可能我们边吃碗里的面,厨师边往碗里做面,防止这样的事就是互斥的原理,但是还有一个问题,就是厨师不能疯狂的一直往碗里方面,碗是盛不下的,或者我们一直吃碗里的面,甚至把碗给吃了。这就是同步要去解决的问题
所以同步就是:在保证互斥的前提下,保证多个线程对临界资源访问的合理性
条件变量
- 线程在加锁之后,判断下临界资源是否可用(这里就像拿到碗之后,可不可以做面,就是当前碗里有没有面,如果没有面,那就可以做面,如果有面就不可以做面,将互斥锁放开然后进行等待,吃面也同理):
- 如果可用:则直接访问临界资源
- 如果不可用:则调用等待接口,让该线程进行等待
1.条件变量的使用原理
- 线程在加锁之后,判断下临界资源是否可用(这里就像拿到碗之后,可不可以做面,就是当前碗里有没有面,如果没有面,那就可以做面,如果有面就不可以做面,将互斥锁放开然后进行等待,吃面也同理):
- 如果可用:则直接访问临界资源
- 如果不可用:则调用等待接口,让该线程进行等待
-
2.条件变量的原理
- 本质上是:,PCB等待队列 (存放在等待的线程的PCB)
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
- 参数:
- pthread_cond_t:
- 条件变量类型
- cond:
- 接收一个条件变量的指针||接收一个条件变量的地址
- attr:
- 表示条件变量的属性信息,传递NULL,采用默认属性
- 2.销毁条件变量:
- int pthread_cond_destroy(pthread_cond_t *cond);
- 3.销毁条件变量:
- int pthread_cond_destroy(pthread_cond_t *cond);
-
3.2等待接口:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
作用:谁调用把谁放在PCB等待队列当中
- 参数:
- cond:
- 条件变量
- mutex:
- 互斥锁
-
3.3唤醒接口:
- 作用:通知PCB等待队列,唤醒在队列中的线程
- int pthread_cond_broadcast (pthread_cond_t *cond);//唤醒PCB等待队列中所有的线程
- int pthread_cond_signal (pthread_cond_t *cond);//唤醒PCB等待队列当中至少一个线程(有可能唤醒两个或者三个或者全部都唤醒)
二、代码展示
这个代码的效果是一会为正一会为负数,这显然是没有对碗这个资源进行合理的使用的
我们再写一个代码,在不用到条件变量的情况下,用if语句来进行
我们运行一下来观察结果
我们可以看到这样确实可以做到吃面和做面是正确着的,那既然这样就可以了为什么还要用到条件变量呢?
缺陷:CPU的利用率很低
假设一个线程拿到的时间片是200ms,在这个时间片范围内,他会先判断,面人如果当前碗里没有面那么他就continue退出这一次循环,但是这时候还没200ms,所以他就会一直在这里重复这个判断和continue,也可能他在刚加完锁时间片就到了,这时候他退出了,然后做面人来了,但是因为锁是加着的,所以做面人也访问不到碗,那么在这个时间片范围内就是在做无用功,时间白白给浪费掉了。
我们这时候加上条件变量再看看看
我们这时候来看看调用堆栈,看看是不是和我们分析的是一样的
我们可以看到,吃面人是在pthread_cond_wait中,做面人是在sleep中。
但是上面代码中写了两个sleep,而且按我们之前的分析,做面人是加不了锁的,那怎么能走到下面的sleep呢?
为了区分到底是在上面还是下面,我们在两个sleep中间打印一句话,如果能打印出来就是下面的sleep中
我们发现了这句话,证明做面线程是在下面的sleep中 ,而且做面人加锁成功了
我们这时候就要知道在pthread_cond_wait中,会进行解锁逻辑
我们之前的现在写的代码已经能够做到吃一碗面做一碗面了,但如果要是有多个吃面人和做面人会怎样呢?
我们这时候在来运行一下看看会怎么样
我们来分析一下为什么会产生这种错误的现象
那这个代码应该怎么改去解决问题呢?
之前那个判断逻辑有问题,有可能做面人被唤醒之后资源还是不可用的,所以要再次进行判断,将if改为while
这时候我们再来运行一下,观察现象
这时候确实是做一碗面吃一碗面,但现在这个程序不结束了,而且也不再进行吃面或者做面了
我们这时候再来看一下这几个线程在干什么呢?
我们神奇的发现,这四个线程都在pthread_cond_wait
我们来分析一下这个现象又是如何产生的
那么怎么解决这个问题呢?
我们可以多加一个条件变量
我们来写代码实现这个逻辑
这时候我们再来运行一下:
我们发现,这时候已经能够顺利的实现功能了,这才算真正支持了多线程
三、条件变量夺命追问
1.条件变量的等待接口第二个参数为什么会有互斥锁?
在函数内部需要解锁操作,如果不解锁就阻塞等待,则其他线程一定拿不到锁
2.pthread_cond_wait的内部是针对互斥锁做了上什么操作?先释放互斥锁还是先将线程放入到PCB等待队列?
解锁, 先放到PCB等待队列再进行解锁
3.线程被唤醒之后会执行什么代码?需要再获取互斥锁吗?
1.从PCB等待队列当中移除
2.抢锁