经典同步问题
信号量机制解决同步问题
信号量是一个二元组(s, q),其中s表示资源的数目(非负整型变量),q为初始状态为空的队列。针对不同的问题,s具有不同的初值和意义。
对信号量的操作:PV操作 PV操作是原语
P操作意为申请,其动作为:先执行 s = s-1,若s>=0,则继续执行;若s<0,则阻塞该进程,并将其插在信号量等待队列中。
V操作意为释放,其动作为:先执行s = s+1,若s>0,则继续执行,若s<=0,则从信号量等待队列中移除第一位进程,使其变成就绪进程。
生产者消费者问题
生产者消费者问题即一组生产者向一组消费者提供产品,他们共享同一个缓冲区。其中生产者需要在缓冲区有空闲的情况下传输产品;消费者需要在缓冲区有产品的情况下消费产品,且多个生成者或消费者需要互斥使用缓冲区。
针对上述问题,我们需要设置两种同步信号量:empty 和 full,其中empty表示缓冲区空闲的数量,初始值为1,full表示缓冲区存在产品的数量,初始值为0;设置一个互斥量 mutex,初始值为1:
首先确定临界资源为缓冲区
生产者需要缓冲区有空闲才能将生产的产品传入缓冲区,因此设置一个同步信号量empty = N(N为缓冲区可容纳的产品数量)
消费者需要缓冲区有产品才能进行消费,因此需设置一个同步信号量full = 0(一开始没有产品)
为控制生产者消费者对临界资源互斥访问,需要设置一个互斥信号量 mutex = 1
//生产者消费者问题
semphore full = 0;
semphore empty = 1;
semphore mutex = 1;
producer()
{
while(1)
{
//生产产品
P(empty);//申请空闲区
P(mutex);//申请缓冲区
向缓冲区运送商品
V(mutex);//释放缓冲区
V(full);//增加缓冲区产品数量
}
}
consumer()
{
while(1)
{
P(full);//等待缓冲区的产品
P(mutex);
消费
V(mutex);
V(empty);//消费完,缓冲区的产品减少,增加空闲区
}
}
P(full)/P(empty)和P(mutex)不可颠倒,否则会发生死锁。
存在多个同类进程就一定需要互斥信号量
读者-写者问题
存在一个共享数据区,有一些只能对数据区进行读取的进程,一些只能对数据区进行写的进程。
要求:
- 任意读者可以同时读取文件
- 一次只能有一个写者对数据区进行写
- 如果写者正在操作,禁止任何进行对数据区进行操作
1、读者优先,即读者进行操作时,写者不能进行写
对于该问题需要设置几个信号量。
首先由于写者要等所有读者操作完后才能操作,所以需要设置一个表示当前正在操作的读者数量的信号量,readcount,初值为0。
由于读者操作时会修改readcount的值,因此需要设置一个互斥信号量rmutex,初值为1。(控制多个读者互斥使用readcount)
设置一个互斥型号量mutex,用于对写者的数据区进行互斥访问。
//读者优先
semphore rmutex = 1;
semphore mutex = 1;
int readcount = 0;
reader()
{
while(1)
{
P(rmutex);//
if(readcount == 0)//如果当前没有读者,则申请读者进入临界区
{
P(mutex);
}
readcount++;//有新的读者,修改readcount的值
V(rmutex);//readcount修改完,释放对readcount的控制权
读操作
P(rmutex);//读者操作结束,需减少readcount的值
readcount--;
if(readcount == 0)//没有读者则允许写者进入临界区
{
V(mutex)
};
V(rmutex);
}
}
writer()
{
while(1)
{
P(mutex);//等待进入临界区
数据写
V(mutex);
}
}
2、公平情况算法
当读者申请访问时,若前面存在写者正在写或正在等待,读者需等待写者完成操作。
为解决此问题,在1的基础上还需要设置一个信号量wmutex,表示是否存在正在写或等待的写者,初值为1
semphore rmutex = 1;
semphore wmutex = 1;
semphore mutex = 1;
int readcount = 0;
reader()
{
while(1)
{
P(wmutex);//申请读
P(rmutex);//申请修改readcount
if(readcount == 0)
{
P(mutex);
}
readcount++;
V(rmutex);
V(wmutex);
//读操作
P(rmutex);
readcount--;
if(readcount == 0)
{
V(mutex);
}
v(rmutex);
}
3、写者优先
写者优先要求:只要等待队列中存在写者,不管何时到达,都优先于读者被唤醒
此时需设置一个互斥信号量readable 用于表示当前是否有写者,控制写者优先于读者,初值为1
设置两个变量,readcount和writecount,分别用于表示当前读写者的数量,初值为0
设置两个信号量,rmutex用于读者互斥,wmutex用于写者互斥(需要修改readcount和writecount的值)
semphore mutex = 1;
semphore wmutex = 1;
semphore rmutex = 1;
semphore readable = 1;
int readcount = 0;
int writecount = 0;
reader()
{
P(reachable);
P(rmutex);
if(readcount == 0)
{
P(mutex);
}
readcount++;
V(rmutex);
V(readable);
//读操作
P(rmutex);
readcount--;
if(readcount == 0 )
{
V(mutex);
}
V(rmutex);
}
write()
{
P(wmutex);
if(writecount == 0)
{
P(readable);
}
writecount++;
V(wmutex);
P(mutex);
//写操作
V(mutex);
P(wmutex);
writecount--;
if(writecount == 0)
{
V(eadable);
}
V(wmutex);
}
哲学家就餐问题
有5个哲学家在圆桌上就餐,每两个人之间有一只筷子。当一个人进餐时,他需同时拿起左右的两只筷子。
在此问题中,筷子是临界资源,不能同时被两个哲学家拿。
对哲学家和筷子进行编号,0-4。当哲学家同时拿起右边的筷子会发生死锁。
解决:规定奇数号哲学家先拿起左边的筷子后再拿起右边的筷子,规定偶数号哲学家先拿起右边的筷子再拿起左边的筷子。
semphore chopstick[5] = {1, 1, 1, 1, 1};
philosphere(int i)
{
if(i&1)//奇数号哲学家
{
P(chopstick[i%5]);
P(chopstick[(i+1)%5]);
//进餐
V(chopstick[i%5]);
V(chopstick[(i+1)%5]);
}
else
{
P(chopstick[(i+1)%5]);
P(chopstick[i%5]);
//进餐
V(chopstick[(i+1)%5]);
V(chopstick[i%5]);
}
}