文章目录
进程同步
1、是什么,主要任务,解决方式,基本概念
是什么: 在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。
主要任务: 对多个相关进程在执行次序上进行协调,使并发执行诸进程之间按照一定的规则(或时序)共享系统资源,并能很好地相互合作,从而使得程序的执行具有可再现性。
解决方式:
- 保证多个进程采用互斥的方式访问临界资源
- 其次要协调相互合作的各个进程的执行次序
基本概念:
- 同步和互斥:
- 同步:指为了完成某个任务,建立多个进程,这些进程在合作的过程中而产生的相互制约的关系
- 互斥:指某个进程对一临界资源访问,其它进程等待的一种相互制约关系
- 两种制约关系:
- 间接相互制约关系:由于共享系统资源,而导致这些并发执行的成宿之间形成的制约的关系
- 直接相互制约关系:由于多个进程之间的合作,而形成的制约关系
- 临界资源(临界区):只一次只允许一个进程使用的共享的资源称之为临界资源,把在每个进程访问临界资源的那段代码称之为临界区
2、两种机制
信号量机制:
- 整型信号量
- 记录型信号量
- 信号量集
管程机制: 管程是由局部于自己的若干公共变量及其说明和所有访问这些公共变量的过程所组成的软件模块。
3、经典问题
3.1、生产者——消费者问题
3.1.1、问题描述
系统中有两个进程,一个进程用于生产资源,一个进程用于消费资源。这两个进程共享一块大小确定的存放资源的区域(缓冲池)。
- 当区域内的资源没有装满时,生产进程可以往里面放资源;当区域内的资源满时,生产进程需等待
- 当区域内的资源不为空时,消费进程可以从里面取出资源;当区域内的资源为空时,消费进程需等待
3.1.2、设计
1、因为两个进程之间的访问,不能同时进行,即生产进程放入资源时,消费进程不能取资源;消费进程取资源时,生产进程不能放入资源, 所以需要一个互斥信号量:mutex,初始值为1,表示进程能直接执行
2、生产进程需要判断缓冲池是不是满的,所以需要一个空缓冲区,empty,初始值为n,当空缓冲区为0时表示已经满了
3、消费进程需要判断缓冲池是不是空的,所以需要一个满缓冲区,full,初始值为0,当满缓冲区为0时,表示缓冲池中没数据
3.1.3、实现
semaphore mutex=1,empty=n,full=0;
void producer(){
do{
wait(empty); //如果empty<=0,等待;否则empty--
wait(mutex); //如果mutex<=0,等待;否则mutex--
//生产
signel(mutex); //mutex++
signel(full); //full++
}while(TRUE);
}
void consumer(){
do{
wait(full); //如果full<=0,等待;否则full--
wait(mutex); //如果mutex<=0,等待;否则mutex--
//生产
signel(mutex); //mutex++
signel(empty); //empty++
}while(TRUE);
}
void main(){
cobegin
producer();consumer();
coend
}
3.2、哲学家进餐
3.2.1、问题描述
一张圆桌上坐着五名哲学家,每两名哲学家之间摆一根筷子,一共五根筷子,一个哲学家只有左手右手都拿到一只筷子才能进餐。进餐完毕,筷子放回原处。
3.2.2、设计
因为有五只筷子,所以筷子为临界资源,即chopstick[5]={1,1,1,1,1};如代码1。
问题: 假如五个哲学家,同时去拿左边的筷子时,五个信号量将都会变为0,将永远拿不到右边的筷子,产生死锁
改进:
- 至多运行有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能进餐,可以增加一个信号量mutex,初始值为4
- 仅当哲学家左右两只筷子都能用时,才允许他那契筷子进餐
- 奇数哲学家先拿左边的,偶数哲学家先拿右边的
3.2.3、实现
代码1(有可能引起死锁):
semaphore chopstick[5]={1,1,1,1,1};
void get(){
do{
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
//eat
signel(chopstick[i]);
signel(chopstick[(i+1)%5]);
}while(TRUE);
}
代码2:
semaphore chopstick[5]={1,1,1,1,1};
void get(){
do{
wait(mutex);
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
//eat
signel(chopstick[i]);
signel(chopstick[(i+1)%5]);
signel(mutex);
}while(TRUE);
}
3.3、读者——写者
3.3.1、问题描述
一个文件可以被读和被写,多个进程操作这一个共享对象
- 因为读操作不会引起文件混乱,所以可以同时有多个进程读
- 写操作会引起文件混乱,所以写操作不允许和其他读操作或者写操作同时访问对象
3.3.2、设计
- 因为可以有多个进程读,所以写入的时候,需要判断当前是否还有进程在读,如果有,则不能写,等待,如果没有,则可以写入,所以需要一个读缓冲池,readcount,初始值为0
- 因为readcount是可以被一个或多个进程访问的临界资源,所以针对以readcount应该设置一个互斥信号量rmutex,初始值为1
- 因为写入的时候,不允许其他操作,因此需要一个写的互斥信号量wmutex,初始值为1
3.3.3、实现
semaphore rmutex=1,wmutex2=1;
int readcount=0;
void reader(){
do{
wait(rmutex);
if(readcount==0)wait(wmutex);
readcount++;
signal(rmutex);
//读
wait(rmutex);
readcount--;
if(readcount==0)signal(wmutex);
signal(rmutex);
}while(TRUE);
}
void writer(){
do{
wait(wmutex);
//写
signal(wmutex);
}while(TRUE);
}
void main(){
cobegin
reader();writer();
coend
}