哲学家就餐问题——信号量/管程

问题
应用程序中包含并发线程的执行时,协调处理共享资源的代表性问题

五位哲学家住在一座房子,他们面前有一张餐桌,每个哲学家生活中只有思考和吃饭。经多年思考,哲学家们认为最有助于思考的事物是意大利面。因缺乏手工功能,每位哲学家需要用两把叉子来吃意大利面。如图:
哲学家就餐问题——信号量/管程_第1张图片
每个哲学家面前有个盘子,左右两边各一个叉子。现在设计一个算法来让哲学家就餐。算法必须互斥(没有两个哲学家能同时用一把叉子),同时要避免死锁、饥饿


分析
每个哲学家都是一个线程,相应的,筷子是被共享资源,所以需要被保护起来

解决

使用信号量解决
每位哲学家首先拿起左边的叉子,然后拿起右边的叉子。在哲学家吃完饭后,这两把叉子又被放回桌子上。

semaphore fork[5]={1};
int i;
void philosopher(int i)
{
	while(1)
	{
		think();
		wait(fork[i]);
		wait(fork[(i+1) mod 5]);
		eat();
		signal(fork[(i+1) mod 5]);
		signal(fork[i]);
	}
}
int main()
{
	parbegin(philosopher(0),philosopher(1),philosopher(2),philosopher(3),philosopher(4));
	return 0;
}

这个解决方案会导致死锁:如果所有的哲学家在同一时刻都感到饥饿,他们都坐下来,都拿起左边的叉子,又都伸手拿右边的叉子,但都没拿到。在这种有损尊严的状态下,所有的哲学家都会处于饥饿状态

为了避免死锁,可以增加一个服务员,仅允许四位哲学家同时进入餐厅,保证至少一位哲学家拿到两把叉子从而避免死锁、饥饿:

semaphore fork[5] = {1};
semaphore room = {4};
int i;
void philosopher(int i)
{
	while(1)
	{
		think();
		wait(room);
		wait(fork[i]);
		wait(fork[(i+1) mod 5]);
		eat();
		signal(fork[(i+1) mod 5]);
		signal(fork[i]);
		signal(room);
	}
}
int main()
{
	parbegin(philosopher(0),philosopher(1),philosopher(2),philosopher(3),philosopher(4));
	return 0;
}

使用管程解决
定义五个条件变量的向量,每把叉子对应一条件变量用以表示叉子可用情况,并且用布尔变量记录叉子是否可用

monitor dining_controller;
cond ForkReady[5];
boolean fork[5]={true};

void get_fork(int pid)
{
	int left = pid;
	int right = (++pid)%5;
	//先取左边
	//只要有一边的筷子不能用,就在条件变量队列等待
	if(!fork(left))
		cwait(ForkReady[left]);
	fork(left)=false;
	if(!fork(right))
		cwait(ForkReady[right]);
	fork(right)=false;
}
//表示两把叉子可用
void release_forks(int pid)
{
	int left = pid;
	int right = (++pid)%5;
	//先放左边
	if(empty(ForkReady[left]))
		fork(left)=true;
	else
		csignal(ForkReady[left]);
	
	if(empty(ForkReady[right]))
		fork(right)=true;
	else
		csignal(ForkReady[right]);
}

void philosopher(int n)
{
	while(1)
	{
		think();
		get_forks(n);
		eat();
		release_forks(n);
	}
}

同一时间只有一个进程能进入管程,一次拿到两只叉子。不会发生死锁

你可能感兴趣的:(#,Linux)