[操作系统] pthread同步互斥:十字路口小车的死锁


       不知道做的对不对,仅供参考。


更新日志:


2016.11.12 更新版本:

解决了程序不能自动退出的bug(卡在死锁检测线程的while)
去掉main中最后一行:pthread_join(check,NULL) 主线程不等待死锁线程结束,程序可以自动退出。


1. 有两条道路双向两个车道,即每条路每个方向只有一个车道,两条道路十字交叉。假设车辆只能向前直行,而不允许转弯和后退。如果有4辆车几乎同时到达这个十字路口,如图(a)所示;相互交叉地停下来,如图(b),此时4辆车都将不能继续向前,这是一个典型的死锁问题。从操作系统原理的资源分配观点,如果4辆车都想驶过十字路口,那么对资源的要求如下:

        l 向北行驶的车1需要象限ab

        l 向西行驶的车2需要象限bc

        l 向南行驶的车3需要象限cd

        l 向东行驶的车4需要象限da

 

 

    我们要实现十字路口交通的车辆同步问题,防止汽车在经过十字路口时产生死锁和饥饿。在我们的系统中,东西南北各个方向不断地有车辆经过十字路口(注意:不只有4辆),同一个方向的车辆依次排队通过十字路口。按照交通规则是右边车辆优先通行,如图(a)中,若只有car1car2car3,那么车辆通过十字路口的顺序是car3->car2->car1。车辆通行总的规则:

        1) 来自同一个方向多个车辆到达十字路口时,车辆靠右行驶,依次顺序通过;

        2) 有多个方向的车辆同时到达十字路口时,按照右边车辆优先通行规则,除非该车在十字路口等待时收到一个立即通行的信号;

        3) 避免产生死锁;

        4) 避免产生饥饿;

        5) 任何一个线程(车辆)不得采用单点调度策略;

        6) 由于使用AND型信号量机制会使线程(车辆)并发度降低且引起不公平(部分线程饥饿),本题不得使用AND型信号量机制,即在上图中车辆不能要求同时满足两个象限才能顺利通过,如南方车辆不能同时判断ab是否有空。

 

编写程序实现避免产生死锁和饥饿的车辆通过十字路口方案并给出详细的设计方案,程序中要有详细的注释

        1) 每一辆车的行为设计为一个单独的线程。由于有4个不同方向的车辆,需要4种不同类型的线程。

        2) 使用pthread互斥锁和条件变量解决车辆的同步与互斥。

        3) 对4个不同方向的车辆,要设置车辆队列条件变量如: queueNorth、queueEastqueueSouthqueueWest。比如说,当一辆车从北方来的时候已经在过十字路口,从北方驶来的车就要等在queueNorth队列中每一个方向都需要一个计数器来跟踪等待排队的车辆数量。

        4) 按照右边车辆优先通行规则,当一辆车在等待通过路口而它右边不断有车辆到达时,这辆车及这个方向车辆队列会导致饥饿。为了防止饥饿,我们刚刚通过路口的A车辆发一个信号给它左边等待B车辆,接下去让B车辆通行需要设置下次通行车辆的条件变量firstNorth firstEast firstSouthfirstWest

        5) 每一车辆到达十字路口时,要检测是否有死锁发生,当发生死锁时,死锁检测线程必须发出一种信号,例如:从北方来的车辆先行。

        6) 假设我们设计的可执行程序文件名为p1-1,可以用'e'、'w'、's'、'n'来标识东西南北4个方向驶来的车辆,程序p1-1运行时有如下显示(你的程序不一定是这样相同的输出):

$ ./ p1-1 nsewwewn

car 4 from West arrives at crossing

car 2 from South arrives at crossing

car 1 from North arrives at crossing

car 3 from East arrives at crossing

DEADLOCK: car jam detected, signalling North to go

car 1 from North leaving crossing

car 3 from East leaving crossing

car 2 from South leaving crossing

car 4 from West leaving crossing

car 6 from East arrives at crossing

car 5 from West arrives at crossing

car 8 from North arrives at crossing

car 5 from West leaving crossing

car 6 from East leaving crossing

car 8 from North leaving crossing

car 7 from West arrives at crossing

car 7 from West leaving crossing


     

      


         

对于题目中要求的车辆优先权的理解:

      

 

        两车都希望占据路口a,由于西边的车(W1车)在北边车(N车)的右边,它具有优先权,将先行。

        W1车离开后,虽然对于N车来说,右边还有车,但是离开的车有必要告诉N车让其优先通行。

  实际上是这样处理的:设计了一个布尔量,每个方向各有一个,当车辆处在就绪状态(如左图的W1车),或者进入第一个路口时,标记布尔量为真,离开第一个路口时,标记布尔量为假。当车辆想要进入第二个路口时,需要检查它右边车的布尔量。如果为真,那么该车停等。

       该布尔量在小车进入就绪队列时就设为真,如果它当时还不为真,就认为这辆车还没有进入就绪状态,也就不是同时到达路口,所以不会出现不同步现象。


        N车过路口d(发现右边有车,等待)

        W1车过路口a

        W1车过路口b(唤醒N车)

        N车过路口a(接到信号,过第二个路口,发送一个信号唤醒W2车)

        W2车过路口a(接到信号,过路口a


新的车谁来唤醒:

 

        新的车有两种唤醒方式:

       1.如果当前方向的车离开后,左边没有车等待,那么它自己唤醒当前方向的一辆车。

       2.如果当前方向的车离开后,左边有车等待,那么它唤醒左边的车,并由左边的车来唤醒其方向的一辆车。

       由于左边要么有车等待,要么没有车等待,所以总会有一个结果被执行,因此我们保证了一定会有新的车被唤醒。


#include <pthread.h>  
#include <stdio.h>  
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define MAX 100

//mutex of the 4 cross
pthread_mutex_t mutex_a;
pthread_mutex_t mutex_b;
pthread_mutex_t mutex_c;
pthread_mutex_t mutex_d;

//mutex of deadlock thread
pthread_mutex_t mutex_e;

//mutex of car waiting(in ready queue)
pthread_mutex_t wait_east;
pthread_mutex_t wait_west;
pthread_mutex_t wait_north;
pthread_mutex_t wait_south;

//mutex of car waiting(in the first cross)
pthread_mutex_t block_east;
pthread_mutex_t block_west;
pthread_mutex_t block_north;
pthread_mutex_t block_south;

//cond of each direction(in the first cross)
pthread_cond_t cond_w;
pthread_cond_t cond_e;
pthread_cond_t cond_n;
pthread_cond_t cond_s;

//cond of each direction(in the ready queue)
pthread_cond_t firstEast;
pthread_cond_t firstWest;
pthread_cond_t firstSouth;
pthread_cond_t firstNorth;

//cond when deadlock happen(to wake up the deadlock thread)
pthread_cond_t cond_deadlock;
//cond when deadlock happen(to wake up the car on the right)
pthread_cond_t cond_lock;

typedef enum { west, north, south, east }dir_t;
dir_t dir;//indicate the direction of the new car that comes to the cross
int size = 0;//count the number of car
int empty;//record the number of empty cross

//indicate the current id of car in each direction
int current_north;
int current_south;
int current_east;
int current_west;

//save all the thread
pthread_t car[MAX];

//a data structure:queue, that descirbes the car in each direction
struct queue
{
	pthread_t thread[MAX];
	int num[MAX];//the id of the car
	int front;//front pointer
	int rear;//rear pointer
	int count;//the number of car in the queue
	queue() {
		front = rear = count = 0;
	}
	void push(int n) {//push a car at the back with id n
		count++;
		rear = (rear + 1) % MAX;
		num[rear] = n;
	}
	int pop() {//pop a car in the front,return the car id
		count--;
		front = (front + 1) % MAX;
		return num[front];
	}
};

//the queue of car
queue car_south;
queue car_east;
queue car_north;
queue car_west;

//if each direction has a car leaving the ready queue /or coming to the first cross
bool is_west;
bool is_south;
bool is_east;
bool is_north;
//if deadlock happens
bool is_deadlock = false;

//wake up all car in the front of the ready queue
void wakeupall()
{
	is_deadlock = false;
	if (car_south.count>0) {
		current_south = car_south.pop();
		//I think this variable has better be designed as a member variable of the class queue
		//but I am too lazy.
		pthread_cond_signal(&firstSouth);
	}
	if (car_north.count>0) {
		current_north = car_north.pop();
		pthread_cond_signal(&firstNorth);
	}
	if (car_west.count>0) {
		current_west = car_west.pop();
		pthread_cond_signal(&firstWest);
	}
	if (car_east.count>0) {
		current_east = car_east.pop();
		pthread_cond_signal(&firstEast);
	}
}
void *car_from_south(void *arg) {
	bool deadlock_flag = false;
	//add a wait lock
	pthread_mutex_lock(&wait_south);
	pthread_cond_wait(&firstSouth, &wait_south);
	pthread_mutex_unlock(&wait_south);

	//has come to the cross
	is_south = true;//set is_come to be true
					//add a lock to the cross
	pthread_mutex_lock(&mutex_a);
	printf("car %d from South arrives at crossing\n", current_south);
	//the resource minus 1
	empty--;
	//indicate current direction
	dir = south;

	bool flag = false;
	//if the resource is used up
	if (empty == 0) {
		//deadlock happened, send a signal to deadlock thread
		pthread_cond_signal(&cond_deadlock);
		//wait until the deadlock is solved
		pthread_cond_wait(&cond_lock, &mutex_a);

		usleep(2000);
		pthread_mutex_lock(&mutex_b);
		pthread_mutex_unlock(&mutex_a);
		printf("car %d from South leaves at crossing\n", current_south);
		is_south = false;
		//resource add 1.
		empty++;
		usleep(2000);
		pthread_mutex_unlock(&mutex_b);
		wakeupall();
		return NULL;
	}

	//if it find a car on its right
	else if (is_east) {
		//  printf("a car on south's right\n");
		flag = true;
		//it will wait for the car to go first
		pthread_cond_wait(&cond_s, &block_south);
		//the car is gone, remember to call the new car
		//and it is wake up, and find that deadlock happend
		//it has to wake up the left car
		if (is_deadlock) {
			usleep(2000);
			pthread_mutex_lock(&mutex_b);
			pthread_mutex_unlock(&mutex_a);
			printf("car %d from South leaves at crossing\n", current_south);
			if (dir == west)pthread_cond_signal(&cond_lock);
			else pthread_cond_signal(&cond_w);
			is_south = false;
			//resource add 1.
			empty++;
			usleep(2000);
			pthread_mutex_unlock(&mutex_b);
			return NULL;
		}
	}
	usleep(2000);

	//come to the second cross
	pthread_mutex_lock(&mutex_b);

	//if flag, remember to call
	if (car_east.count>0 && flag) {
		current_east = car_east.pop();
		pthread_cond_signal(&firstEast);
	}

	//release the lock
	pthread_mutex_unlock(&mutex_a);
	is_south = false;
	//resource add 1.
	empty++;
	printf("car %d from South leaves at crossing\n", current_south);

	//if a car is waiting on the left, let it go
	//and the waiting car in the queue is waked up by it
	if (is_west)pthread_cond_signal(&cond_w);
	//or it's waked up by itself
	else if (!is_south && car_south.count>0) {
		current_south = car_south.pop();
		pthread_cond_signal(&firstSouth);
	}
	usleep(2000);

	//unlock
	pthread_mutex_unlock(&mutex_b);
}
void *car_from_east(void *arg) {
	//add a wait lock
	pthread_mutex_lock(&wait_east);
	pthread_cond_wait(&firstEast, &wait_east);
	pthread_mutex_unlock(&wait_east);
	//has come to the cross
	is_east = true;//setis_come to be true
				   //add a lock to cross
	pthread_mutex_lock(&mutex_b);
	printf("car %d from East arrives at crossing\n", current_east);
	empty--;
	dir = east;
	bool flag = false;
	if (empty == 0) {
		pthread_cond_signal(&cond_deadlock);
		pthread_cond_wait(&cond_lock, &mutex_b);

		usleep(2000);
		pthread_mutex_lock(&mutex_c);
		pthread_mutex_unlock(&mutex_b);
		printf("car %d from East leaves at crossing\n", current_east);
		is_east = false;
		//resource add 1.
		empty++;
		usleep(2000);
		pthread_mutex_unlock(&mutex_c);
		wakeupall();
		return NULL;
	}
	// if it find a car on its right
	else if (is_north) {
		flag = true;
		//it will wait for the car to go first
		pthread_cond_wait(&cond_e, &block_east);
		//it will wait for the car to go first

		//the car is gone, remember to call the new car
		//and it is wake up, and find that deadlock happend
		//it has to wake up the left car
		if (is_deadlock) {
			usleep(2000);
			pthread_mutex_lock(&mutex_c);
			pthread_mutex_unlock(&mutex_b);
			printf("car %d from East leaves at crossing\n", current_east);
			//if it find a car on its left is the deadlock car,wake up it
			if (dir == south)pthread_cond_signal(&cond_lock);
			//otherwise wake up the one on the left
			else pthread_cond_signal(&cond_s);
			is_east = false;
			//resource add 1.
			empty++;
			usleep(2000);
			pthread_mutex_unlock(&mutex_c);
			return NULL;
		}
	}

	usleep(2000);
	//come to the second cross
	pthread_mutex_lock(&mutex_c);

	//if flag, remember to call
	if (!is_north && car_north.count>0 && flag) {
		current_north = car_north.pop();
		pthread_cond_signal(&firstNorth);
	}

	//release the lock
	empty++;
	pthread_mutex_unlock(&mutex_b);
	is_south = false;

	printf("car %d from East leaves at crossing\n", current_east);

	if (is_east)pthread_cond_signal(&cond_s);
	else if (!is_east && car_east.count>0) {
		current_east = car_east.pop();
		pthread_cond_signal(&firstEast);
	}

	usleep(2000);

	pthread_mutex_unlock(&mutex_c);
}
void *car_from_north(void *arg) {
	//  printf("create north\n");
	//add a wait lock
	pthread_mutex_lock(&wait_north);
	pthread_cond_wait(&firstNorth, &wait_north);
	pthread_mutex_unlock(&wait_north);
	is_north = true;
	pthread_mutex_lock(&mutex_c);
	printf("car %d from North arrives at crossing\n", current_north);
	empty--;
	dir = north;

	bool flag = false;
	if (empty == 0) {
		//deadlock happened, send a signal to deadlock thread
		pthread_cond_signal(&cond_deadlock);
		//wait until the deadlock is solved

		pthread_cond_wait(&cond_lock, &mutex_c);

		pthread_cond_signal(&cond_e);
		usleep(2000);
		pthread_mutex_lock(&mutex_d);
		pthread_mutex_unlock(&mutex_c);
		printf("car %d from West leaves at crossing\n", current_north);
		is_north = false;
		//resource add 1.
		empty++;
		usleep(2000);
		pthread_mutex_unlock(&mutex_d);

		wakeupall();
		return NULL;
	}
	//if it find a car on its right
	else if (is_west) {
		// printf("a car on north's right\n");
		flag = true;
		//it will wait for the car to go first
		pthread_cond_wait(&cond_n, &block_north);
		//the car is gone, remember to call the new car
		//and it is wake up, and find that deadlock happend
		//it has to wake up the left car
		if (is_deadlock) {
			usleep(2000);
			pthread_mutex_lock(&mutex_d);
			pthread_mutex_unlock(&mutex_c);
			printf("car %d from North leaves at crossing\n", current_north);
			if (dir == east)pthread_cond_signal(&cond_lock);
			else pthread_cond_signal(&cond_e);
			is_north = false;
			//resource add 1.
			empty++;
			usleep(2000);
			pthread_mutex_unlock(&mutex_d);
			return NULL;
		}
	}
	usleep(2000);

	pthread_mutex_lock(&mutex_d);
	//if flag, remember to call the right car
	if (car_west.count>0 && flag) {
		current_west = car_west.pop();
		pthread_cond_signal(&firstWest);
	}

	empty++;
	pthread_mutex_unlock(&mutex_c);
	is_north = false;
	printf("car %d from North leaves at crossing\n", current_north);

	if (is_east)pthread_cond_signal(&cond_e);
	//or it's waked up by itself
	else if (!is_north && car_north.count>0) {
		current_north = car_north.pop();
		pthread_cond_signal(&firstNorth);
	}

	usleep(2000);

	pthread_mutex_unlock(&mutex_d);


}

void *car_from_west(void *arg) {
	//add a wait lock
	pthread_mutex_lock(&wait_west);
	pthread_cond_wait(&firstWest, &wait_west);
	pthread_mutex_unlock(&wait_west);


	//has come to the cross
	is_west = true;//set is_come to be true
	pthread_mutex_lock(&mutex_d);
	printf("car %d from West arrives at crossing\n", current_west);

	//the resource minus 1
	empty--;
	//indicate current direction
	dir = west;

	bool flag = false;
	//if the resource is used up
	if (empty == 0) {
		//deadlock happened, send a signal to deadlock thread
		pthread_cond_signal(&cond_deadlock);
		//wait until the deadlock is solved

		pthread_cond_wait(&cond_lock, &mutex_d);
		usleep(2000);
		pthread_mutex_lock(&mutex_a);
		pthread_mutex_unlock(&mutex_d);
		printf("car %d from West leaves at crossing\n", current_west);
		is_south = false;
		//resource add 1.
		empty++;
		usleep(2000);
		pthread_mutex_unlock(&mutex_a);
		wakeupall();
		return NULL;
	}
	//if it find a car on its right
	else if (is_south) {
		flag = true;
		//it will wait for the car to go first

		pthread_cond_wait(&cond_w, &block_west);
		//the car is gone, remember to call the new car
		//and it is wake up, and find that deadlock happend
		//it has to wake up the left car
		if (is_deadlock) {
			usleep(2000);
			pthread_mutex_lock(&mutex_a);
			pthread_mutex_unlock(&mutex_d);
			printf("car %d from West leaves at crossing\n", current_west);
			if (dir == north)pthread_cond_signal(&cond_lock);
			else pthread_cond_signal(&cond_n);
			is_west = false;
			//resource add 1.
			empty++;
			usleep(2000);
			pthread_mutex_unlock(&mutex_a);
			return NULL;
		}
	}
	usleep(2000);
	//come to the second cross
	pthread_mutex_lock(&mutex_a);

	//if flag, remember to call
	if (!is_north && car_north.count>0 && flag) {
		current_north = car_north.pop();
		pthread_cond_signal(&firstNorth);
	}

	empty++;
	pthread_mutex_unlock(&mutex_d);
	is_west = false;

	printf("car %d from West leaves at crossing\n", current_west);
	if (is_north)pthread_cond_signal(&cond_n);
	else if (!is_west && car_west.count>0) {
		current_west = car_west.pop();
		pthread_cond_signal(&firstWest);
	}

	usleep(2000);
	pthread_mutex_unlock(&mutex_a);
}

void *check_dead_lock(void *arg) {
	//wait....
	usleep(4000);
	//at first wake up all the car;
	wakeupall();

	while (1) {
		pthread_mutex_lock(&mutex_e);
		//wait for deadlock
		pthread_cond_wait(&cond_deadlock, &mutex_e);
		//deadlock happen,set is_deadlock true
		is_deadlock = true;
		printf("DEADLOCK: car jam detected, signalling");
		//ask a car to go first,according to the latest car direction.
		switch (dir) {
		case north: {printf(" East "); pthread_cond_signal(&cond_e); break; }
		case east: {printf(" South "); pthread_cond_signal(&cond_s); break; }
		case west: {printf(" North "); pthread_cond_signal(&cond_n); break; }
		case south: {printf(" West "); pthread_cond_signal(&cond_w); break; }
		}
		printf("to go\n");

		pthread_mutex_unlock(&mutex_e);
	}
}

int main(int argc, char** argv) {

	int num[100];

	pthread_t check;//a thread for deadlock checking

	//initialize(it seems that it doesn't matter whether I intialize)
	pthread_cond_init(&cond_lock, NULL);
	pthread_cond_init(&cond_deadlock, NULL);
	pthread_cond_init(&cond_w, NULL);
	pthread_cond_init(&cond_s, NULL);
	pthread_cond_init(&cond_e, NULL);
	pthread_cond_init(&cond_n, NULL);
	pthread_cond_init(&firstEast, NULL);
	pthread_cond_init(&firstWest, NULL);
	pthread_cond_init(&firstSouth, NULL);
	pthread_cond_init(&firstNorth, NULL);

	pthread_mutex_init(&mutex_a, NULL);
	pthread_mutex_init(&mutex_b, NULL);
	pthread_mutex_init(&mutex_c, NULL);
	pthread_mutex_init(&mutex_d, NULL);
	pthread_mutex_init(&mutex_e, NULL);
	pthread_mutex_init(&wait_north, NULL);
	pthread_mutex_init(&wait_south, NULL);
	pthread_mutex_init(&wait_east, NULL);
	pthread_mutex_init(&wait_west, NULL);
	pthread_mutex_init(&block_north, NULL);
	pthread_mutex_init(&block_east, NULL);
	pthread_mutex_init(&block_west, NULL);
	pthread_mutex_init(&block_south, NULL);
	char s[100];
	scanf("%s", s);
	int len = strlen(s);
	empty = 4;
	//create the thread
	for (int i = 0; i<len; i++)num[i] = i + 1;
	for (int i = 0; i<len; i++) {
		switch (s[i]) {
		case 'w': {
			is_west = true;
			car_west.push(num[i]);
			car[size++] = car_west.thread[car_west.front];
			pthread_create(&car_west.thread[car_west.front],
				NULL, car_from_west, NULL);
			break;
		}
		case 'e': {
			is_east = true;
			car_east.push(num[i]);
			car[size++] = car_east.thread[car_east.front];
			pthread_create(&car_east.thread[car_east.rear],
				NULL, car_from_east, NULL);
			break;
		}
		case 's': {
			is_south = true;
			car_south.push(num[i]);
			car[size++] = car_south.thread[car_south.rear];
			pthread_create(&car_south.thread[car_south.rear],
				NULL, car_from_south, NULL);
			break;
		}
		case 'n': {
			is_north = true;
			car_north.push(num[i]);
			car[size++] = car_north.thread[car_north.rear];
			pthread_create(&car_north.thread[car_north.rear],
				NULL, car_from_north, NULL);
			break;
		}
		}
	}

	pthread_create(&check, NULL, check_dead_lock, NULL);
	//join the thread
	for (int i = 0; i<size; i++) {
		pthread_join(car[i], NULL);
	}
}


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值