哲学家就餐问题是一个了解和练习线程间同步的非常好的小例子,题为 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; }
程序运行后结果如下图,每个哲学家都可以正常的就餐了: