使用 pthread、互斥锁和条件变量解决哲学家就餐问题

Posted

技术标签:

【中文标题】使用 pthread、互斥锁和条件变量解决哲学家就餐问题【英文标题】:Solve Dining Philosophers Problem Using pthreads, mutex locks, and condition variables 【发布时间】:2020-07-29 23:51:53 【问题描述】:

我正在尝试使用 pthread、互斥锁和条件变量在 C 中实现哲学家就餐问题。

它需要一个命令行参数来指定程序应该运行多长时间。我必须使用 sleep 功能来完成此操作。 每位哲学家最多可以吃 10 顿饭。一旦他们吃完 10 顿饭,pthread 就应该终止。 在设定的时间结束时,pthreads 需要终止,并且应该打印每个哲学家吃的饭数。

我的输出有一些问题:

    使主函数休眠在命令行中输入的秒数似乎不会使输出有所不同。 大多数哲学家都在为程序的大多数执行而挨饿。 当我打印出哲学家正在思考或吃饭时,会出现“哲学家 5”,即使应该只有 0-4 位哲学家。

这是我的代码:

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

//Function declarations
void *pickup_forks(void * philosopher_number);
void *return_forks(void * philosopher_number);
void test(int philosopher_number);
int left_neighbor(int philosopher_number);
int right_neighbor(int philosopher_number);
double think_eat_time(void);
void think(double think_time);
void eat(double eat_time);

//Constants to be used in the program.
#define PHILOSOPHER_NUM 5
#define MAX_MEALS 10
#define MAX_THINK_EAT_SEC 3

//States of philosophers.
enum THINKING, HUNGRY, EATING state[PHILOSOPHER_NUM];

//Array to hold the thread identifiers.
pthread_t philos_thread_ids[PHILOSOPHER_NUM];

//Mutex lock.
pthread_mutex_t mutex;

//Condition variables.
pthread_cond_t cond_vars[PHILOSOPHER_NUM];

//Array to hold the number of meals eaten for each philosopher.
int meals_eaten[PHILOSOPHER_NUM];

int main(int argc, char *argv[])

    //Ensure correct number of command line arguments.
    if(argc != 2)
    
        printf("Please ensure that the command line argument 'run_time' is passed.\n");
    
    else
    
        //Set command line argument value to variable run_time;
        double run_time = atof(argv[1]);

        //Initialize arrays.
        int i;
        for(i = 0; i < PHILOSOPHER_NUM; i++)
        
            state[i] = THINKING;
            pthread_cond_init(&cond_vars[i], NULL);
            meals_eaten[i] = 0;
        


        //Initialize the mutex lock.
        pthread_mutex_init(&mutex, NULL);

        //Join the threads.
        for(i = 0; i < PHILOSOPHER_NUM; i++)
        
            pthread_join(philos_thread_ids[i], NULL);
        

        //Create threads for the philosophers.
        for(i = 0; i < PHILOSOPHER_NUM; i++)
        
            pthread_create(&philos_thread_ids[i], NULL, pickup_forks, (void *)&i);
        

        sleep(run_time);

        for(i = 0; i < PHILOSOPHER_NUM; i++)
        
            pthread_cancel(philos_thread_ids[i]);
        

        //Print the number of meals that each philosopher ate.
        for(i = 0; i < PHILOSOPHER_NUM; i++)
        
            printf("Philosopher %d: %d meals\n", i, meals_eaten[i]);
        

    

    return 0;


void *pickup_forks(void * philosopher_number)

    int loop_iterations = 0;
    int pnum = *(int *)philosopher_number;

    while(meals_eaten[pnum] < MAX_MEALS)
    
        printf("Philosoper %d is thinking.\n", pnum);
        think(think_eat_time());

        pthread_mutex_lock(&mutex);
        state[pnum] = HUNGRY;
        test(pnum);

        while(state[pnum] != EATING)
        
            pthread_cond_wait(&cond_vars[pnum], &mutex);
        
        pthread_mutex_unlock(&mutex);

        (meals_eaten[pnum])++;

        printf("Philosoper %d is eating meal %d.\n", pnum, meals_eaten[pnum]);
        eat(think_eat_time());

        return_forks((philosopher_number));

        loop_iterations++;
    


void *return_forks(void * philosopher_number)

    pthread_mutex_lock(&mutex);
    int pnum = *(int *)philosopher_number;

    state[pnum] = THINKING;

    test(left_neighbor(pnum));
    test(right_neighbor(pnum));

    pthread_mutex_unlock(&mutex);


int left_neighbor(int philosopher_number)

    return ((philosopher_number + (PHILOSOPHER_NUM - 1)) % 5);


int right_neighbor(int philosopher_number)

    return ((philosopher_number + 1) % 5);


void test(int philosopher_number)

    if((state[left_neighbor(philosopher_number)] != EATING) && 
        (state[philosopher_number] == HUNGRY) &&
        (state[right_neighbor(philosopher_number)] != EATING))
    
        state[philosopher_number] = EATING;
        pthread_cond_signal(&cond_vars[philosopher_number]);
    


double think_eat_time(void)

    return ((double)rand() * (MAX_THINK_EAT_SEC - 1)) / (double)RAND_MAX + 1;


void think(double think_time)

    sleep(think_time);


void eat(double eat_time)

    sleep(eat_time);

这是使用 10 秒的输出:

~$ gcc dining_philos.c -o dp -lpthread
~$ ./dp 10
Philosoper 1 is thinking.
Philosoper 2 is thinking.
Philosoper 3 is thinking.
Philosoper 4 is thinking.
Philosoper 5 is thinking.
Philosoper 2 is eating meal 1.
Philosoper 4 is eating meal 1.
Philosoper 2 is thinking.
Philosoper 4 is thinking.
Philosoper 2 is eating meal 2.
Philosoper 4 is eating meal 4.
Philosoper 4 is thinking.
Philosoper 2 is thinking.
Philosoper 2 is eating meal 3.
Philosoper 4 is eating meal 5.
Philosoper 2 is thinking.
Philosoper 4 is thinking.

Philosopher 0: 2 meals
Philosopher 1: 0 meals
Philosopher 2: 3 meals
Philosopher 3: 0 meals
Philosopher 4: 5 meals

我将不胜感激。谢谢!

【问题讨论】:

【参考方案1】:

pthread_create 传递一个指针作为其最后一个参数。该指针必须在线程运行时有效,而不仅仅是在创建时有效。您正在使用指向循环变量 i 的指针,该指针已消失。

也不确定您为什么在 pthread_create 之前调用 pthread_join,但我预计它会返回错误(您没有检查)。

【讨论】:

好的。我创建了另一个包含哲学家编号的数组,并在 pthread_create 中为哲学家 i 传递了该数组的第 i 个索引。这似乎成功了。谢谢! 不知道是什么意思

以上是关于使用 pthread、互斥锁和条件变量解决哲学家就餐问题的主要内容,如果未能解决你的问题,请参考以下文章

linux 互斥锁和条件变量

LinuxC线程pthread线程同步进程同步-互斥量信号量条件变量读写锁文件锁

Linux下的互斥锁和条件变量

Pthread 超时

Linux系统编程-(pthread)线程通信(条件变量)

信号量,互斥锁,读写锁和条件变量的区别