今天看操作系统的时候,在进程管理一章又看到了经典的进程同步问题。进程同步问题确实比较复杂,因为要保证进程推进顺序合法,可能会使用到多个信号量,而且很多p,v操作的顺序也不
可颠倒。好在进程同步有几个经典问题,如果搞清楚了,对其他问题也可以举一反三。
生产者消费者问题:
分析:1.生产者消费者共用一个缓冲区,生产者向其中投入产品,消费者从中取出产品,并消费。所以需要定义一个缓冲区,这里用数组这种数据结构便可以。
2.设置两个记录性信号量,一个说明空缓冲区数量,用empty表示,初值为n,一个说明满缓冲区数量,用full表示,初值为0
3.因为生产者消费者不唯一,所以需要定义一个互斥信号量mutex,初值为1.
程序如下:
读者写者问题:(读者优先)
描述:可以允许多个读者同时读;读者和写者之间必须互斥;读者优先,所以只有读者数目为0时候,写者才能够写
思路:1.信号量解决的是互斥的问题,但是写者何时能写,是一个条件判断,需要判断读者数目,所以设置一个变量readcount,记录读者的个数。
2.对读者的数量更新时,需要设置一个信号量用于互斥,防止多个读者同时读,从而同时更新读者数量
3.读者与写者之间互斥,所以也需要互斥信号量
读者写者问题:(写者优先)
描述:与读者优先的区别是,一旦有写者,则后续读者需要等待,唤醒时优先唤醒写者。
思路:关键问题是处理:如果当前有一个读者或者写者在读/写,怎样让排队的那些读者/写者保持写者优先。
可颠倒。好在进程同步有几个经典问题,如果搞清楚了,对其他问题也可以举一反三。
生产者消费者问题:
分析:1.生产者消费者共用一个缓冲区,生产者向其中投入产品,消费者从中取出产品,并消费。所以需要定义一个缓冲区,这里用数组这种数据结构便可以。
2.设置两个记录性信号量,一个说明空缓冲区数量,用empty表示,初值为n,一个说明满缓冲区数量,用full表示,初值为0
3.因为生产者消费者不唯一,所以需要定义一个互斥信号量mutex,初值为1.
程序如下:
Semaphore empty = n;
Semaphore full = 0; //empty与full是此消彼长的关系,为了方便,设置两个信号量
Semaphore mutex = 1;
Array Buffer[0...n-1];
int index = 0; //用来记录缓冲区当前的索引
Producer() {
while(true) {
p(empty);
p(mutex);
produce a product p;
Buffer[index] = p;
index++;
V(mutex);
V(full);
}
}
Consumer(){
while(true) {
p(full);
p(mutex);
consume Buffer[index];
index--;
V(mutex);
V(empty);
}
}
需要注意的问题:p(mutex)与p(full)/p(empty)顺序不能颠倒,V(mutex)与v(full)/v(empty)顺序则随便。
读者写者问题:(读者优先)
描述:可以允许多个读者同时读;读者和写者之间必须互斥;读者优先,所以只有读者数目为0时候,写者才能够写
思路:1.信号量解决的是互斥的问题,但是写者何时能写,是一个条件判断,需要判断读者数目,所以设置一个变量readcount,记录读者的个数。
2.对读者的数量更新时,需要设置一个信号量用于互斥,防止多个读者同时读,从而同时更新读者数量
3.读者与写者之间互斥,所以也需要互斥信号量
int readcount = 0;
semaphore rwmutex = 1;
semaphore rmutex= 1; //用于读者进程更新readcount时候互斥
reader() {
p(rmutex);
readcount++;
if(readcount == 1) p(rwmutex); //只要读者数量达到1的时候,将使用rwmutex互斥,阻止新的写者进入
v(rmutex);
读数据;
p(rmutex);
readcount--;
if(readcount == 0) v(rwmutex);
v(rmutex);
}
writer() {
p(rwmutex);
写数据;
v(rwmutex);
}
读者写者问题:(写者优先)
描述:与读者优先的区别是,一旦有写者,则后续读者需要等待,唤醒时优先唤醒写者。
思路:关键问题是处理:如果当前有一个读者或者写者在读/写,怎样让排队的那些读者/写者保持写者优先。
semaphore rwmutex = 1;
semaphore rmutex = 10; //初始值设为10,表示最多允许10个读者同时读
reader()
{
p(rwmutex); //用于读写互斥,如果有写者在写数据或者在排队,则读者就进不去
p(rmutex); //占用一个读者名额
v(rwmutex);
读数据;
v(rmutex);
}
writer()
{
p(rwmutex); //禁止新的读者进入
for(i=1; i < 10; i++) {
p(rmutex); //只要有一个写者进程进入时,第一件事就是将所有读者的名额全部占用,那些还没占用的读者名额会被立即占用,而正在读数据的读者,在释放了名额之后,名额会被立即占用
}
写数据;
for(i = 1; i < 10; i++) {
v(rmutex); //恢复rmutex的值为10
}
v(rwmutex);
}
以后有时间再继续分析其他几个经典同步问题