条件变量——用于线程需要检查某一情况才能继续运行的情况。
本章从自旋锁引入,提出了自旋锁自旋占用系统资源的问题,提出了条件变量的概念。
条件变量是一个显式队列,当某些执行状态不满足时,线程自己加入队列,等待执行条件被满足。
有两个相关操作,wait()和signal(),休眠时调用wait,唤醒时用signal。
调用wait有一个参数是互斥量,调用时是上锁的,调用wait释放锁,并让调用线程休眠(原子地)。当被唤醒时,重新持有锁,再返回调用。
signal是没有这个参数的,只有条件变量一个参数。
状态变量的必要性,就是循环检查的那个语句使用的变量——如果子进程创建后立刻运行,signal不会唤醒任何线程(此时无休眠),但是因为唤醒的已经使用过了,这时候执行父进程,就会调用wait,但是没有唤醒,会一直休眠。
锁的必要性——在判断语句判断之后准备睡眠之前被中断,会长眠不醒。
生产者/消费者问题
两种语义——Hoare语义,保证被唤醒的线程立刻执行。
Mesa语义,唤醒后不会立刻执行。几乎所有系统都采用Mesa语义。
对于书p254的方案,用if判断状态变量,只有一个生产者一个消费者不会出问题,但如果超过一个线程就会出问题。
问题一、因为signal之后不会立刻执行,所有当存在两个消费者时可能会在第一个消费者完成状态判断之后其被中断,当生产者改变状态变量后,另一个消费者直接执行消费缓冲区,导致第一个消费者继续执行时,因为状态判断了,所有直接执行消费,但此时缓冲区已经没有可以消费的了。
解决方式就是if改while,Mesa语义总是使用while。
问题二、生产者消费者使用同一条件变量,如果存在消费者和生产者同时休眠,不知道应该唤醒哪一个。
解决方案就是使用两个条件变量。生产者线程等待条件变量empty,发信号给fill。消费者线程反之。
最终的实现方式为了提高并发使用了数组缓存,判断是否休眠改成了数组是否满以及是否空。
覆盖条件
介绍了一个用广播的方式唤醒所有休眠线程的方法。