多线程互斥锁解决哲学家就餐问题

哲学家就餐问题是一个了解和练习线程间同步的非常好的小例子,题为 5 个哲学家(线程)围成一桌就餐,但是只有 5 只筷子(锁),一个人想要吃饭就必须要拥有左侧的筷子(锁1)和右侧的筷子(锁2)才能吃饭。每一个哲学家刚进桌前都持有了自己左侧的筷子,这样所有人只有一只筷子都无法就餐,所以就要想办法去拿右侧的筷子,而因为右侧的筷子被别人持有,所以无法拿到,这个时间就成了死锁状态。所以必须要有一个解锁的条件,那就是在哲学家尝试去拿右侧筷子的时候,如果失败了,那么将自己左手边的筷子放下,此时这个哲学家左侧人就可以持有他原来左手边的筷子来就餐了。实现的具体步骤和代码如下:


【代码实现】

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

#define THREAD_COUNT 5
pthread_mutex_t mutex[THREAD_COUNT];

// 用以给线程传递数据用的结构体
struct tag_thread_arg
{
	int pthread_idx;		// 线程的编号,对应哲学家编号
	pthread_mutex_t left;	// 左侧筷子
	pthread_mutex_t right;	// 右侧筷子
};

void* pthreadFunc(void* arg)
{
	// 得到传递进来的结构体
	struct tag_thread_arg* local = 
		(struct tag_thread_arg*)arg;

	while (1)
	{
		// 左侧筷子锁,循环拿左侧的锁,拿到以后再向下走
		while (pthread_mutex_trylock(&local->left) != 0);
		// 判断右侧筷子是否可以锁定
		if (pthread_mutex_trylock(&local->right) == 0)
		{
			// 可以锁定则吃饭
			int sec = rand() % 5 + 1;
			sleep(sec);
			printf("thread %d mixi %ds\n", local->pthread_idx, sec);
			// 吃完把两把锁都释放
			pthread_mutex_unlock(&local->right);
			pthread_mutex_unlock(&local->left);
		}
		else
		{
			// 如果不能锁定右侧筷子,则把自己左侧筷子放开,让别人先吃
			pthread_mutex_unlock(&local->left);
		}
	}
	return (void*)0;
}

int main(int argc, char* argv[])
{
	srand(time(NULL));
	pthread_t pid[5];
	int i;

	// 初始化5个互斥量
	for (i = 0; i < THREAD_COUNT; i++)
	{
		pthread_mutex_init(&mutex[i], NULL);
	}

	// 定义5个结构体用来给线程传递数据
	struct tag_thread_arg arg[5];
	// 创建5个线程,并把结构体内容初始化后传递进去
	for (i = 0; i < THREAD_COUNT; i++)
	{
		arg[i].pthread_idx = i;							//线程ID,对应哲学家编号
		arg[i].left = mutex[i];							//锁1,对应哲学家左侧筷子
		arg[i].right = mutex[(i + 1) % THREAD_COUNT];	//锁2,对应哲学家右侧筷子
		pthread_create(&pid[i], NULL, pthreadFunc, (void*)&arg[i]);	// 创建线程
	}

	// 等待所有线程结束
	for (i = 0; i < 5; i++)
	{
		pthread_join(pid[i], NULL);
	}

	// 回收互斥量资源
	for (i = 0; i < THREAD_COUNT; i++)
	{
		pthread_mutex_destroy(&mutex[i]);
	}

	return 0;
}

程序运行后结果如下图,每个哲学家都可以正常的就餐了:

2015-07-05_173820

 

评论