记录型信号量定义
//用结构体定义一个记录型信号量,value是代表资源数目的整型变量,list为进程链表指针,用于链接所有等待该资源的进程
typedef struct{
int value;
struct process_control_block *list;
}semaphore;
wait(semaphore *S)
{
S->value--;
if(S->value<0)
block(S->list);//进程调用block原语进行自我阻塞,放弃处理机
} //并插入到信号量链表S->list中
signal(semaphore *S)
{
S->value++;
if(S->value<=0)
wakeup(S->list);//将S->list链表中第一个等待进程唤醒
}
生产者—消费者问题
问题
一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息。
生产者与消费者全互斥
int in=0,out=0;
item buffer[n];
semaphore mutex=1,empty=n,full=0;
void producer()
{
do{
produce an item nextp;
...
wait(empty);
wait(mutex);
buffer[in]=nextp;
in=(in+1)%n;
signal(mutex);
signal(full);
}while(TRUE);
}
void consumer()
{
do
{
wait(full);
wait(mutex);
nextc=buffer[out];
out=(out+1)%n;
signal(mutex);
signal(empty);
consume the item in nextc;
...
}while(TRUE);
}
void main()
{
cobegin
producer();
consumer();
coend;
}
注意,在每个程序中的多个wait不可以颠倒顺序。
生产者、消费者内部互斥
对上面的代码稍作修改,引入两个mutex,分别控制生产者与消费者对buffer[n]的访问。
void producer()
{
do{
produce an item nextp;
...
wait(empty);
wait(mutex1);
buffer[in]=nextp;
in=(in+1)%n;
signal(mutex1);
signal(full);
}while(TRUE);
}
void consumer()
{
do
{
wait(full);
wait(mutex2);
nextc=buffer[out];
out=(out+1)%n;
signal(mutex2);
signal(empty);
consume the item in nextc;
...
}while(TRUE);
}
当缓冲区无限大时
基于全互斥,若缓冲区无限大,生产者生产的时候不需要等待缓冲区有空间,删除wait(empty),消费者中的signai(empty)也对应删除。
void producer()
{
do{
produce an item nextp;
...
wait(mutex);
buffer[in]=nextp;
in=(in+1)%n;
signal(mutex);
signal(full);
}while(TRUE);
}
void consumer()
{
do
{
wait(full);
wait(mutex);
nextc=buffer[out];
out=(out+1)%n;
signal(mutex);
consume the item in nextc;
...
}while(TRUE);
}
利用管程解决
首先建立一个管程。
Monitor producerconsumer
{
item buffer[N];
int in,out;
condition notfull,not empty;
int count;
public:
void put(item x)
{
if(count>=N)
cwait(notfull);
buffer[in]=x;
in=(in+1)%N;
count++;
csignal(notempty);
}
}
void get(item x)
{
if(count<=0)
cwait(notempty);
x=buffer[out];
out=(out+1)%N;
count--;
csignal(notfull);
}
{in=0;out=0;count=0;}
}PC;
void producer()
{
item x;
while(TRUE)
{
...
produce an item in nextp;
PC.put(x);
}
}
void consumer()
{
item x;
while(TRUE)
{
PC.get(x);
consume the item in nextc;
...
}
}
void main()
{
cobegin
producer();
consumer();
coend;
}
哲学家进餐问题
问题
哲学家就餐问题可以这样表述,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。
用一个信号量表示一支筷子。
semaphore chopsticks[5]={1,1,1,1,1};
do{//描述某个哲学家的活动
wait(chopsticks[i]);
wait(chopsticks[i+1]%5);
...
//eat
...
signal(chopsticks[i]);
signal(chopsticks[i+1]%5);
...
//think
...
}while(TRUE);
以上描述会引起死锁,可以采用一定的规则解决。如可以利用AND信号量解决。
semaphore chopsticks[5]={1,1,1,1,1};
do{
...
//think
...
Swait(chopsticks[i],chopsticks[i+1]%5);
...
//eat
...
Signal(chopsticks[i],chopsticks[i+1]%5);
}while(TRUE);
读者—写者问题
问题
(1)允许多个读者同时执行读操作;
(2)不允许读者、写者同时操作;
(3)不允许多个写者同时操作。
Reader和Writer的同步问题分为读者优先、弱写者优先(公平竞争)和强写者优先三种情况,它们的处理方式不同。
读者优先
条件
对于读者优先,应满足下列条件:
如果新读者到:
①无读者、写者,新读者可以读;
②有写者等待,但有其它读者正在读,则新读者也可以读;
③有写者写,新读者等待。
如果新写者到:
①无读者,新写者可以写;
②有读者,新写者等待;
③有其它写者,新写者等待。
实现
int readcount=0; //用于记录当前的读者数量
semaphore rmutex=1; //用于保护更新readcount变量时的互斥
semaphore rwmutex=1; //用于保证读者和写者互斥地访问文件
void Writer()
{//写者进程
do{
wait(rwmutex);
perform write operation;
signal(wmutex);
}while(TRUE);
}
Reader()
{// 读者进程
do{
wait(rmutex);
if(readcount==0)
wait(rwmutex);
readcount++
signal(rmutex);
...
perform read operation;
...
wait(rmutex);
readcount--;
if(readcount==0)
signal(rwmutex);
signal(rmutex);
}while(TRUE);
}
void main()
{
cobegin
Reader();
Writer();
coend;
}
若最多只允许RN个读者读,引入信号量L=RN,利用信号量集机制。
int RN;
semaphore L=RN,mx=1;
void Reader()
{
do{
Swait(L,1,1);
Swait(mx,1,0);//开关作用,只要无写者进入,mx=1,reader随时可
... //以进入读操作
perform read operation;
...
Ssignal(L,1);
}while(TRUE);
}
void Writer()
{
do{
Swait(mx,1,1;L,RN,0); //既无写者在写(mx=1)又无读者在读
perform write operation;//(L=RN)
Ssignal(mx,1);
}while(TRUE);
}
void main()
{
cobegin
Reader();
Writer();
coend;
}