经典进程同步问题
1 )生产者—消费者问题(合作互斥)
是相互合作的进程关系的一种抽象,同时也包含着进程的互斥。
分析 :
- 空缓冲区不为空,生产者才能将生产的数据放入缓冲区中。
- 满缓冲区不为空,消费者才能从缓冲区中取数据。
- 为了保证进程同步,生产者与消费者不可以同时访问缓冲区。
1)利用记录型信号量解决
in ;out;//生产者消费者指针
mutex//缓冲池互斥信号量
empty//缓冲池空缓冲区数量
full//缓冲池满缓冲区数量
/*buffer数组是用来表示缓冲区的,而empty和full是用来表示缓冲区
的空闲和使用情况的*/
//生产者
int in=0, out=0;
item buffer[n];
semaphore mutex=1, empty=n, full=0;
void proceducer() {
do {
producer an item nextp;//生产者生产的产品放入nextp中
...
wait(empty);//查看有无空闲的缓冲区
wait(mutex);//使进程互斥
buffer[in] =nextp;//把nextp中的产品送往buffer(in)
in :=(in+l) % n;//指针偏移
signal(mutex);//释放资源
signal(full);//满缓冲区加1
}while(TRUE);
}
//消费者
void consumer() {
do {
wait(full);//查看缓冲区是否有数据
wait(mutex);//进程互斥
nextc= buffer[out];//从buffer(out)中取出产品放入nextc
out =(out+l) % n;//指针偏移
signal(mutex);//释放资源
signal(empty);//空缓冲区加1
consumer the item in nextc;//消费nextp中产品
}while(TRUE);
}
void main() {
cobegin
proceducer(); consumer();
coend
}
注意 :
- 互斥的wait(mutex)和signal(mutex)必须成对地出现;
- 对资源信号量empty和full的wait和signal操作,同样需要成对地出现,但它们分别处于不同的程序中。
- 每个程序中的多个wait操作顺序不能颠倒。应先执行对资源信号量的wait操作,然后再执行对互斥信号量的wait操作,否则可能引起进程死锁。
2)利用AND型信号量解决
即同时实现wait(empty)与wait(mutex)操作,与实现signal(mutex)与signal(full)操作。
- Swait(empty, mutex) 代替 wait(empty)和 wait(mutex);
- Ssignal(mutex, full) 代替 signal(mutex)和 signal(full);
- Swait(full, mutex) 代替 wait(full)和 wait(mutex)
- Ssignal(mutex, empty) 代替Signal(mutex)和Signal(empty)
代码如下 :
int in=0, out=0;
item buffer[n];
semaphore mutex=1, empty=n, full=0;
void proceducer() {
do {
producer an item nextp;
Swait(empty, mutex);
buffer[in] =nextp;
in :=(in+1) % n;
Ssignal(mutex, full);
}while(TRUE);
}
void consumer() {
do {
Swait(full, mutex);
nextc= buffer[out];
out =(out+l) % n;
Ssignal(mutex, empty);
consumer the item in nextc;
}while(TRUE);
}
2)哲学家进餐问题(资源耗尽,避免死锁)
临界资源的使用,避免死锁状况的产生。
解决方法 :
-
记录型信号量:至多只允许有四位哲学家同时去拿左边的筷子, 最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子, 从而使更多的哲学家能够进餐。
-
AND型信号量: 仅当哲学家的左、 右两只筷子均可用时,才允许他拿起筷子进餐。
-
数学方法:规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是1、 2号哲学家竞争1号筷子; 3、 4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数号筷子, 获得后, 再去竞争偶数号筷子, 最后总会有一位哲学家能获得两只筷子而进餐。
1)利用记录型信号量解决
//以第i位哲学家举例
semaphore chopstick[5]={ 1, 1, 1, 1, 1};
do {
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
//eat
signal(chopstick[i]);
signal(chopstick[(i+l)%5]);
//think
}while[TRUE];
2)利用AND型信号量解决
semaphore chopstick chopstick[5]={ 1, 1, 1, 1, 1);
do {
...
//think
Sswait(chopstick[(i+1)%5], chopstick[i]);
//eat
Ssignal(chopstick[(i+1)%5], chopstick[i]);
}while[TRUE];
3)读者—写者问题(互斥问题)
互斥进程的管理 :
分析 :
- 允许多个读者同时执行读操作
- 不允许读者、写者同时操作
- 不允许多个写者同时操作
1)记录型信号量解决 (写者优先)
读进程只要看到有其他读进程正在访问文件,就可以继续作读访问;写进程必须等待所有读进程都不访问时才能写文件,即使写进程可能比一些读进程更早提出申请。所以以上解法实际是 读者优先 的解法。
wmutex//读写互斥
readcount//读进程数目
rmutex//读进程计数
semaphore rmutex= 1, wmutex= 1;
int readcount=0;
//读进程
void reader() {
do {
wait(rmutex);//申请读,对读数更改
if (readcount==0) wait(wmutex);/*第一个进入进行检验,防止正
在写的情况*/
readcount++;
signal(rmutex);//释放读
...
perform read operation;
...
wait(rmutex);//申请读,对读数更改
readcount--;//读者数减1
if (readcount==0) signal(wmutex);//最后一个离开,唤醒写进程
signal(rmutex);//释放读
}while(TRUE);
}
//写进程
void writer()
{
do {
wait(wmutex);//申请写互斥信号量
perform write operation;
signal(wmutex);//释放信号量
}while(TRUE);
}
void main(){
cobegin
reader(); writer();
coend
}
2)利用信号量集解决有限同时读
- 允许RN个读者同时执行读操作
- 不允许读者、写者同时操作
- 不允许多个写者同时操作
int RN;
semaphore L=RN, mx=1;
void reader()
{
do {
Swait(L, 1, 1);//L,下限为1,需求量为1
Swait(mx, 1, 0);/*互斥信号量,下限为1,需求为0,当有写时mx为0,
读失败*/
...
perform read operation;
...
Ssignal(L, 1);//释放资源
}while(TRUE);
}
void writer()
{
do {
Swait(mx, 1, 1; L, RN, 0);/*既无writer写(mx=1),也无
reader读(L=RN),writer进入临界区写*/
perform write operation;
Ssignal(mx, 1);
}while(TRUE);
}
3)读者—写者写操作优先:
每个读进程最开始都要申请一下S信号量,之后在真正做读操作前即让出(使得写进程可以随时申请到 S)。而只有第一个写进程需要申请 S,之后就一直占着不放了,直到所有写进程都完成后才让出。等于只要有写进程提出申请就禁止读进程排队,变相提高了写进程的优先级。
semaphore rmutex=1,wmutex= 1, S=1, w=1;
int Readcount=0, Writecount=0;
//读进程
void reader() {
do {
wait(S);//对读进程加以限制
wait(rmutex);//读计数
if(Readcount=0)wait(wmutex);//在无读者的情况下检验是否在写
Readcount++;//读者数加1
signal(rmutex);//释放读计数
signal(S);//加以限制
...
perform read operation;
...
wait(rmutex);
Readcount--;
if(Readcount=0)signal(wmutex);//最后一个;离开唤醒写进程
signal(rmutex)
}while(TRUE);
}
void writer{
do {
wait(w);
if(Writecount=0)wait(S);//此时新的读者无法再进入读
Writecount++;
siganl(w);
wait(wmutex);//写进程被唤醒后进行写操作
...
perform write operation;//此时一直占据着信号量S,写进程一旦进入则顺序执行
...
signal(wmutex);
wait(w);
Writecount--;
if(Writecount=0)signal(S);//当所有写进程完成才归还信号量,读者可以再次进入临界区
siganl(w);
}while(TRUE);
}
4)读者—写者问题公平竞争
方法相同,读写排队,谁先到拿到S,谁先操作。
void reader() {
do {
wait(S);//对读进程加以限制
wait(rmutex);//读计数
if(Readcount=0)wait(wmutex);//在无读者的情况下检验是否在写
Readcount++;//读者数加1
signal(rmutex);//释放读计数
signal(S);//加以限制
...
perform read operation;
...
wait(rmutex);
Readcount--;
if(Readcount=0)signal(wmutex);//最后一个;离开唤醒写进程
signal(rmutex)
}while(TRUE);
}
void writer(){
while(1){
wait(S);//读写同时排队,谁先来谁先用
wait(wmutex);
signal(S);
...
perform write operation
...
signal(wmutex);
}
}