哲学家问题
有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了(死锁)。哲学家进餐问题是一个多线程运用的经典例子,涉及到线程同步/互斥,临界区访问问题以及死锁问题。我们要做的就是防止死锁。
线程中的信号量
这里的信号量和我另一篇博客中进程间的通信的博客里的信号量是不同的,区别可以参考我找到的一篇博客进程的信号量和线程的信号量。
信号量是一个计数器,可以用来控制多个进程或线程对共享资源的访问。它常作为一种锁机制,防止某进程或线程在访问共享资源时,其他进程或线程也在访问,因此主要作为进程间或同一进程间不同线程之间的同步机制。当信号量大于0时表示可以使用的资源数量,小于0时表示等待使用资源的进程或线程个数。信号量的值仅能通过PV操作来改变。
无名信号量的定义:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
P操作:使信号量-1
#include <semaphore.h>
int sem_wait(sem_t *sem);
sem为指向信号量结构的一个指针;
V操作:使信号量+1
#include <semaphore.h>
int sem_post(sem_t *sem);
sem为指向信号量结构的一个指针;
正常代码(可能发生死锁现象)
每一根筷子都是一个信号量,有人拿就进行P操作对拿起的筷子-1,吃完进行V操作对放下的筷子+1。
我们这里把哲学家设置都先拿左边的筷子,拿到后再拿右边的筷子,没拿到就等待别人放下筷子。
哲学家吃饭.c
/***************************************
* 正常哲学家吃饭
* 可能会造成死锁
* ************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
sem_t sem[6];
void thread_1(void *arg)
{
int sum = 0;
sem_wait(&sem[1]);
printf("111拿起1筷子\t");
sem_wait(&sem[2]);
printf("111拿起2筷子\t");
printf("哲学家1吃完!\t");
sem_post(&sem[1]);
printf("111释放1筷子\t");
sem_post(&sem[2]);
printf("111释放2筷子\n");
}
void thread_2(void *arg)
{
sem_wait(&sem[2]);
printf("222拿起2筷子\t");
sem_wait(&sem[3]);
printf("222拿起3筷子\t");
printf("哲学家2吃完!\t");
sem_post(&sem[2]);
printf("222释放2筷子\t");
sem_post(&sem[3]);
printf("222释放3筷子\n");
}
void thread_3(void *arg)
{
sem_wait(&sem[3]);
printf("333拿起3筷子\t");
sem_wait(&sem[4]);
printf("333拿起4筷子\t");
printf("哲学家3吃完!\t");
sem_post(&sem[3]);
printf("333释放3筷子\t");
sem_post(&sem[4]);
printf("333释放4筷子\n");
}
void thread_4(void *arg)
{
//printf("*");
sem_wait(&sem[4]);
printf("444拿起4筷子\t");
//printf("A");
sem_wait(&sem[5]);
printf("444拿起5筷子\t");
printf("哲学家4吃完!\t");
sem_post(&sem[4]);
printf("444释放4筷子\t");
sem_post(&sem[5]);
printf("444释放5筷子\n");
}
void thread_5(void *arg)
{
//printf("**");
sem_wait(&sem[5]);
printf("555拿起5筷子\t");
//printf("AA");
sem_wait(&sem[1]);
printf("555拿起1筷子\t");
printf("哲学家5吃完!\t");
sem_post(&sem[5]);
printf("555释放5筷子\t");
sem_post(&sem[1]);
printf("555释放1筷子\n");
}
int main()
{
pthread_t thread1;
pthread_t thread2;
pthread_t thread3;
pthread_t thread4;
pthread_t thread5;
sem_init(&sem[1], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[2], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[3], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[4], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[5], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
int ret;
//printf("%d\n%d\n%d\n%d\n%d\n",sem[1],sem[2],sem[3],sem[4],sem[5]);
ret = pthread_create(&thread1, NULL, (void *)thread_1, NULL);
if(ret != 0)
{
printf("Thread_1 wrong!\n");
}
ret = pthread_create(&thread2, NULL, (void *)thread_2, NULL);
if(ret != 0)
{
printf("Thread_2 wrong!\n");
}
ret = pthread_create(&thread3, NULL, (void *)thread_3, NULL);
if(ret != 0)
{
printf("Thread_3 wrong!\n");
}
ret = pthread_create(&thread4, NULL, (void *)thread_4, NULL);
if(ret != 0)
{
printf("Thread_4 wrong!\n");
}
ret = pthread_create(&thread5, NULL, (void *)thread_5, NULL);
if(ret != 0)
{
printf("Thread_5 wrong!\n");
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
pthread_join(thread4, NULL);
pthread_join(thread5, NULL);
sem_destroy(&sem[1]);
sem_destroy(&sem[2]);
sem_destroy(&sem[3]);
sem_destroy(&sem[4]);
sem_destroy(&sem[5]);
return 0;
}
解法1
利用信号量设置最多只能有4为哲学家同时拿左边筷子
/***************************************
* 哲学家吃饭
* 解法1:设置信号量限制最多只有4人同时拿起左边筷子
* ************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
sem_t sem[6];
void thread_1(void *arg)
{
int sum = 0;
sem_wait(&sem[1]);
printf("111拿起1筷子\n");
sem_wait(&sem[0]); //防止5人同时那左边筷子
sem_wait(&sem[2]);
printf("111拿起2筷子\n");
printf("\n哲学家1吃完!\n");
sem_post(&sem[1]);
printf("111释放1筷子\n");
sem_post(&sem[0]);
sem_post(&sem[2]);
printf("111释放2筷子\n");
}
void thread_2(void *arg)
{
sem_wait(&sem[2]);
printf("222拿起2筷子\n");
sem_wait(&sem[0]); //防止5人同时那左边筷子
sem_wait(&sem[3]);
printf("222拿起3筷子\n");
printf("\n哲学家2吃完!\n");
sem_post(&sem[2]);
printf("222释放2筷子\n");
sem_post(&sem[0]);
sem_post(&sem[3]);
printf("222释放3筷子\n");
}
void thread_3(void *arg)
{
sem_wait(&sem[3]);
printf("333拿起3筷子\n");
sem_wait(&sem[0]); //防止5人同时那左边筷子
sem_wait(&sem[4]);
printf("333拿起4筷子\n");
printf("\n哲学家3吃完!\n");
sem_post(&sem[3]);
printf("333释放3筷子\n");
sem_post(&sem[0]);
sem_post(&sem[4]);
printf("333释放4筷子\n");
}
void thread_4(void *arg)
{
//printf("*");
sem_wait(&sem[4]);
printf("444拿起4筷子\n");
sem_wait(&sem[0]); //防止5人同时那左边筷子
//printf("A");
sem_wait(&sem[5]);
printf("444拿起5筷子\n");
printf("\n哲学家4吃完!\n");
sem_post(&sem[4]);
printf("444释放4筷子\n");
sem_post(&sem[0]);
sem_post(&sem[5]);
printf("444释放5筷子\n");
}
void thread_5(void *arg)
{
//printf("**");
sem_wait(&sem[5]);
printf("555拿起5筷子\n");
sem_wait(&sem[0]); //防止5人同时那左边筷子
//printf("AA");
sem_wait(&sem[1]);
printf("555拿起1筷子\n");
printf("\n哲学家5吃完!\n");
sem_post(&sem[5]);
printf("555释放5筷子\n");
sem_post(&sem[0]);
sem_post(&sem[1]);
printf("555释放1筷子\n");
}
int main()
{
pthread_t thread1;
pthread_t thread2;
pthread_t thread3;
pthread_t thread4;
pthread_t thread5;
sem_init(&sem[0], 0, 4); //信号量设置为4,表示只能最多4个人拿左边的筷子
sem_init(&sem[1], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[2], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[3], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[4], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[5], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
int ret;
//printf("%d\n%d\n%d\n%d\n%d\n",sem[1],sem[2],sem[3],sem[4],sem[5]);
ret = pthread_create(&thread1, NULL, (void *)thread_1, NULL);
if(ret != 0)
{
printf("Thread_1 wrong!\n");
}
ret = pthread_create(&thread2, NULL, (void *)thread_2, NULL);
if(ret != 0)
{
printf("Thread_2 wrong!\n");
}
ret = pthread_create(&thread3, NULL, (void *)thread_3, NULL);
if(ret != 0)
{
printf("Thread_3 wrong!\n");
}
ret = pthread_create(&thread4, NULL, (void *)thread_4, NULL);
if(ret != 0)
{
printf("Thread_4 wrong!\n");
}
ret = pthread_create(&thread5, NULL, (void *)thread_5, NULL);
if(ret != 0)
{
printf("Thread_5 wrong!\n");
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
pthread_join(thread4, NULL);
pthread_join(thread5, NULL);
sem_destroy(&sem[0]);
sem_destroy(&sem[1]);
sem_destroy(&sem[2]);
sem_destroy(&sem[3]);
sem_destroy(&sem[4]);
sem_destroy(&sem[5]);
return 0;
}
解法2
对哲学家编号,奇数先拿左边偶数先那右边
/***************************************
* 哲学家吃饭
* 解法2:奇数先拿左边,偶数先那右边
* ************************************/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
sem_t sem[6];
void thread_1(void *arg)
{
int sum = 0;
sem_wait(&sem[1]);
printf("111拿起1筷子\n");
sem_wait(&sem[2]);
printf("111拿起2筷子\n");
printf("\n哲学家1吃完!\n");
sem_post(&sem[1]);
printf("111释放1筷子\n");
sem_post(&sem[2]);
printf("111释放2筷子\n");
}
void thread_2(void *arg)
{
sem_wait(&sem[3]);
printf("222拿起3筷子\n");
sem_wait(&sem[2]);
printf("222拿起2筷子\n");
printf("\n哲学家2吃完!\n");
sem_post(&sem[3]);
printf("222释放3筷子\n");
sem_post(&sem[2]);
printf("222释放2筷子\n");
}
void thread_3(void *arg)
{
sem_wait(&sem[3]);
printf("333拿起3筷子\n");
sem_wait(&sem[4]);
printf("333拿起4筷子\n");
printf("\n哲学家3吃完!\n");
sem_post(&sem[3]);
printf("333释放3筷子\n");
sem_post(&sem[4]);
printf("333释放4筷子\n");
}
void thread_4(void *arg)
{
//printf("*");
sem_wait(&sem[5]);
printf("444拿起5筷子\n");
sem_wait(&sem[4]);
printf("444拿起4筷子\n");
//printf("A");
printf("\n哲学家4吃完!\n");
sem_post(&sem[5]);
printf("444释放5筷子\n");
sem_post(&sem[4]);
printf("444释放4筷子\n");
}
void thread_5(void *arg)
{
//printf("**");
sem_wait(&sem[5]);
printf("555拿起5筷子\n");
//printf("AA");
sem_wait(&sem[1]);
printf("555拿起1筷子\n");
printf("\n哲学家5吃完!\n");
sem_post(&sem[5]);
printf("555释放5筷子\n");
sem_post(&sem[1]);
printf("555释放1筷子\n");
}
int main()
{
pthread_t thread1;
pthread_t thread2;
pthread_t thread3;
pthread_t thread4;
pthread_t thread5;
sem_init(&sem[1], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[2], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[3], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[4], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
sem_init(&sem[5], 0, 1); //信号量设置为1,表示一次只能一个人拿该筷子
int ret;
//printf("%d\n%d\n%d\n%d\n%d\n",sem[1],sem[2],sem[3],sem[4],sem[5]);
ret = pthread_create(&thread1, NULL, (void *)thread_1, NULL);
if(ret != 0)
{
printf("Thread_1 wrong!\n");
}
ret = pthread_create(&thread2, NULL, (void *)thread_2, NULL);
if(ret != 0)
{
printf("Thread_2 wrong!\n");
}
ret = pthread_create(&thread3, NULL, (void *)thread_3, NULL);
if(ret != 0)
{
printf("Thread_3 wrong!\n");
}
ret = pthread_create(&thread4, NULL, (void *)thread_4, NULL);
if(ret != 0)
{
printf("Thread_4 wrong!\n");
}
ret = pthread_create(&thread5, NULL, (void *)thread_5, NULL);
if(ret != 0)
{
printf("Thread_5 wrong!\n");
}
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
pthread_join(thread4, NULL);
pthread_join(thread5, NULL);
sem_destroy(&sem[1]);
sem_destroy(&sem[2]);
sem_destroy(&sem[3]);
sem_destroy(&sem[4]);
sem_destroy(&sem[5]);
return 0;
}