No43.进程间的同步与互斥---哲学家问题

1.水果问题

  • 桌上有一只盘子,每次只能放一个水果,而且盘子最多装一个水果,爸爸只往里面放苹果,妈妈只往里面放香蕉,儿子只吃香蕉,女儿只吃苹果,用P、V操作实现四人正确活动的程序。
semaphore empty=1;  //判断盘子是否为空的信号量
semaphore apple=0,banana=0;   //是否有苹果,香蕉的信号量

Father:
    P(empty)
    向盘子里放苹果;
    V(apple);
Mother:
    P(empty)
    向盘子里放香蕉;
    V(banana);
Daughter:
    P(apple)
    取盘子里的苹果;
    V(empty);
Son:
    P(banana)
    取盘子里的香蕉;
    V(empty);
  • 桌上有一只盘子,每次只能放和取出一个水果,而且盘子最多装2个水果,爸爸只往里面放苹果,妈妈只往里面放香蕉,两个儿子只吃香蕉,两个女儿只吃苹果,用P、V操作实现六人正确活动的程序。

用一个信号量对盘子里的水果个数进行计数,用另一个信号量用来控制对盘子的互斥访问(因为每次只能放和取出一个水果),还有苹果和香蕉的信号量

semaphore empty=2;  //对盘子里的水果个数进行计数
semaphore mutex=1;  //控制对盘子的互斥访问
semaphore apple=0,banana=0;   //是否有苹果,香蕉的信号量

Father:
    P(empty);
    P(mutex);
    向盘子里放苹果;
    V(mutex);
    v(apple);
Mother:
    P(empty);
    P(mutex);
    向盘子里放香蕉;
    V(mutex);
    v(banana);
Daughter i(i=1,2):
    P(apple);
    P(mutex);
    取盘子里的苹果;
    V(mutex);
    v(empty);
Son i(i=1,2):
    P(banana);
    P(mutex);
    取盘子里的香蕉;
    V(mutex);
    v(empty);

2.公交车问题

  • 设公交车上有一位司机和一位售票员,它们的活动如下:
司机
启动车辆
正常行驶
到站停车
售票员
关车门
售票
开车门

规定:只有当售票员关车门后司机才能启动车辆,只有当司机停车门后售票员才能开车门。用P、V操作实现两人正确活动的程序。

semaphore start=0;    //可以开车的信号量
semaphore open=0;     //可以开门的信号量

司机:
P(start);
启动车辆;
正常行驶;
到站停车;
V(open);

售票员:
关车门;
V(start);   //因为只有关车门后才能启动车辆
售票;
P(open);
开车门;

3.哲学家问题

  • 哲学家问题:
    五个哲学家围在一个圆桌旁边吃饭,每相邻两个哲学家之间有且只有1个叉子可以使用。规定:哲学家都是很讲究的,必须抓起自己旁边的两个叉子才会吃饭,否则就算饿死也不吃,哲学家不吃的时候都在思考哲学问题(我是谁?我和动物的区别是什么?我和机器的区别是什么…),当然,哲学家也不能抓着叉子就不放,吃饭的时间是有限的。怎样才能让我们的哲学家都能吃到饭,比如五个人同时抓起一个叉子,那么五个人都没得吃。

下面这个代码就会产生五个人都没得吃的情况:

take_fork()和put_fork()函数内部都有P,V操作,也就是说同一个叉子不能被两个人同时拿起

#define N 5

void philosopher(int i)   //第i个哲学家
{
while(true){
think();    //我要思考问题!
take_fork();    //准备要吃饭了,拿起第i个叉子,
//假设哲学家被编好号,这个i就是哲学家的编号,
//它对应的是抓起左边的叉子
take_fork((i+1)%N);    //抓起右边的叉子
eat();    //我开动了
put_fork(i);    //放下左边的叉子
put_fork((i+1)%N);  //放下右边的叉子
}
}

解决办法有很多,比如下面四种都可行:
1.同一时间只允许四个哲学家同时拿起叉子
2.规定拿起第一个叉子时,编号为奇数的哲学家先拿起自己左边的叉子,而编号为偶数的哲学家先拿起自己右边的叉子
3.更暴力一些,把上面代码think()后面的五句保护起来,设置mutex,规定一次只允许一个哲学家吃饭。
4.规定:当且仅当旁边的两个叉子都可以用时,才允许哲学家拿叉子,否则就一直等待。

下面代码是对第4种方法的实现:

#define N 5     //哲学家的数量
#define LEFT (i+N-1)%N    //哲学家i的左边哲学家编号
#define RIGHT (i+1)%N     //哲学家i的右边哲学家编号
//下面三个宏都用来表示哲学家的状态
#define THINKING 0    //思考中的哲学家
#define HUNGRY 1      //饿肚子的哲学家
#define EATING 2      //进食中的哲学家
typedef int semaphore;
int state[N];    //记录哲学家现在的状态,这个状态就是前面的三个宏
semaphore mutex=1;
semaphore s[N];    //为每个哲学家设置一个信号量

void philosopher(int i)    //i是哲学家编号
{
    while(true)
    {
		think();
		take_forks(i);
		eat();
		put_forks(i);
	}
}

void take_forks(int i)
{
down(&mutex);
state[i]=HUNGRY;
test(i);     //尝试一下能不能同时拿起左右两只叉子
up(&mutex);
down(&s[i]);    //如果在之前的test里不能拿起两个叉子,那么会在这条语句被锁住
}

void put_forks(int i)
{
down(&mutex);
state[i]=THINKING;
test(LEFT);    //看看左边的哲学家有没有食欲
test(RIGHT);   //看看右边的哲学家有没有食欲
up(&mutex);
}

void test(int i)
{
	if(state[i[==HUNGRY && state[LEFT]!=EATING && state[RIGHT]!=EATING)
	{
		state[i]=EATING;
		up(&s[i]);
	}
}

4.读者写者问题

  • 读者和写者问题

读者和写者都可以访问一个数据库,读者只能读,写者只能写,规定如下:
1.允许任意多个读者同时读数据
2.同一时间只允许一个写者修改数据
3.写者和读者不能同时访问数据

用P、V操作实现的思路:
(把这个数据库想象成图书馆,方便陈述理解)
1.偏向读者型:
(1)当读者到来时,只要有读者在读数据,或者没有人访问数据,这个新的读者都可以读数据
(2)当写者到来时,只有没人访问数据时才能对数据进行写

2.偏向写者型:
(1)当读者到来时,如果前面有写者在等待进入图书馆,读者就不能进去,当然里面有写者也不能进入
(2)当写者到来时,里面有读者依然不能进入,只有里面没人时才能进。

typedef int semaphore;
semaphore mutex=1;    //控制对读者的数量进行加减的信号量
semaphore db=1;       //图书馆的信号量,database
int rc=0;             //图书馆里读者的数量

void reader()
{
	while(true)
	{
	//进入
		down(&mutex);
		rc++;
		if(rc==1)
		down(&db);
		up(&mutex);
		
		read_db();   //读取数据

    //离开
    	down(&mutex);
    	rc--;
    	if(rc==0)
    	up(&db);
    	up(&mutex);
	}
}

void writer()
{
	while(true)
	{
		down(&db);
		write_db();
		up(&db);
	}
}

5.总结

  • P、V操作必须成对出现
  • 当为互斥操作时,它们同处于同一进程。
    当为同步操作时,它们不在同一进程中。
  • 一个同步P操作与一个互斥P操作在一起时同步P操作在互斥P操作前。
  • 两个V操作的顺序无关紧要。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值