学习了使用POSIX信号量来解决哲学家问题,代码及注释如下:
/** @file philosopher_thread.c
* @brief 模拟哲学家思考和吃饭问题
* @note
* @author
* @date
* @version v1.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#define PHL_DEBUG
#ifdef PHL_DEBUG
#define PHL_PRT(...) do{printf("[%s]",__func__);printf(""__VA_ARGS__);}while(0)
#else
#define PHL_PRT(...)
#endif
#define PHILOSHOPHER_NUM 5
sem_t fork_num[PHILOSHOPHER_NUM];
/**@fn void think_random_time()
* @brief 随机数生成模拟哲学家思考吃饭过程
* @return 无
*/
void think_eat_random_time()
{
int time;
time = rand()%PHILOSHOPHER_NUM + 1; // 思考时间,1 ~ 5
printf("time = %d s\n", time);
sleep(time);
}
/**@fn void* philosopher_think_eat(void *arg)
* @brief 模拟哲学家的思考和吃饭过程
* @param void *arg 表示第几个哲学家
* @note 具体过程是首先一个哲学家判断左右手是否可以获取叉子,如果有的话则获取
叉子,如果是两只手都有叉子,则开始吃饭,如果是一只手获取,则再等待
1 s 后查看另一只手是否能够获取叉子,能够的话获取吃饭,否则释放掉自己
手中的叉子,思考
* @return 无
*/
void* philosopher_think_eat(void *arg)
{
int i = *((int *)arg);
// 定义左右手变量来表示哲学家左右手的叉子
int lefthand = (i-1+PHILOSHOPHER_NUM)%PHILOSHOPHER_NUM;
int righthand = i;
// 定义一下变量表示哲学家获取左右手叉子是否成功
int leftOK = 0;
int rightOK = 0;
while (1)
{
PHL_PRT("philosopher %d is going to think ", i);
think_eat_random_time();
// 获取左右手的叉子
leftOK = sem_trywait( &fork_num[lefthand] );
rightOK = sem_trywait( &fork_num[righthand] );
/* 哲学家通过获取左右手的叉子来判断是否可以吃饭 */
if ( 0 == (leftOK || rightOK) ) // 都为 0 表示 OK ,可以吃饭
{
printf("philosopher %d going to eat ", i);
think_eat_random_time();
// 释放左右手叉子
sem_post(&fork_num[lefthand]);
sem_post(&fork_num[righthand]);
}
else if ( 0 == leftOK) // 左手获取了叉子,等待 1 s,判断右手看是否有叉子
{
sleep(1);
rightOK = sem_trywait( &fork_num[righthand] ); // 再次获取右手叉子
if ( 0 == rightOK ) // 获取成功
{
printf("philosopher %d going to eat ", i);
think_eat_random_time();
// 释放左右手叉子
sem_post(&fork_num[lefthand]);
sem_post(&fork_num[righthand]);
}
}
else if ( 0 == rightOK ) // 左手获取了叉子,等待 1 s,判断右手看是否有叉子
{
sleep(1);
leftOK = sem_trywait( &fork_num[lefthand] ); // 再次获取右手叉子
if ( 0 == leftOK ) // 获取成功
{
printf("philosopher %d going to eat ", i);
think_eat_random_time();
// 释放左右手叉子
sem_post(&fork_num[lefthand]);
sem_post(&fork_num[righthand]);
}
}
else // 表示左右手都没有获得叉子,直接进入思考
{
continue;
}
}
}
/**@fn void philosopher( int i )
* @brief 创建 i 个哲学家并模拟吃通心粉和思考过程
* @param int i; 传入的哲学家个数
* @return 无
*/
void philosopher( int i )
{
int num = i;
int j = 0, k = 0;
pthread_t td[num];
int err[num];
int tmp = 0;
for ( j=0; j<num; j++ )
{
err[j] = pthread_create(&td[j], NULL, philosopher_think_eat, &j);
if ( err[j] < 0 )
{
perror("philosopher create error!\n");
for ( k=0; k<PHILOSHOPHER_NUM; k++ )
{
if ( sem_destroy(&fork_num[k]) < 0 ) // 删除信号量
{
perror("sem_init error!\n");
exit (-1);
}
}
exit( -1 );
}
tmp ++;
usleep(100); // 睡眠100ms是为了防止创建的线程传入的 j 值都一样
}
}
/**@fn int main(int argc, char* argv[])
* @brief 函数入口
* @param 无
* @return 0-true,-1-false
*/
int main(int argc, char* argv[])
{
int i = 0;
srand((unsigned)time(NULL)); // 种下时间种子,产生随机数表示哲学家的思考和吃饭时间
for ( i=0; i<PHILOSHOPHER_NUM; i++ )
{
if ( sem_init(&fork_num[i], 0, 1) < 0 ) // 初始化信号量为 1,且共享
{
perror("sem_init error!\n");
exit (-1);
}
}
philosopher(PHILOSHOPHER_NUM); // 调用函数模拟思考吃饭过程
while (1)
{
printf("----------1s passed-------------\n");
sleep(1); // 主函数睡眠
}
for ( i=0; i<PHILOSHOPHER_NUM; i++ )
{
if ( sem_destroy(&fork_num[i]) < 0 ) // 初始化信号量为 1,且共享
{
perror("sem_init error!\n");
exit (-1);
}
}
return 0;
}