有没有一种方法可以创建一个线程来检查C中的其他线程,Dining哲学家的实现



我有一个关于C中线程的问题,我知道要创建线程,需要函数pthread_create,我目前正在处理用餐哲学家问题,在这个问题的实现中,我必须看看哲学家是否死于饥饿。

我测试了我的程序,它运行得很好,但为了看看哲学家是否去世了,我创建了另一个线程,如果哲学家去世了,它会一直运行并检查。如果一个哲学家从最后一顿饭起有一段时间不吃东西,他就会饿死。

定义程序和标头的一般结构。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
struct t_phil;
typedef struct t_info t_info;
typedef struct t_phil t_phil;
typedef struct t_info
{
int num_of_phil;
t_phil *philo;
int min_dinner;
pthread_mutex_t *mutex;
int plate_eaten;
int num_of_dead_phil;
int time_to_die;
int time_to_sleep;
int time_to_eat;
pthread_mutex_t t_mut;
} t_info;
typedef struct t_phil
{
int number;
int has_eaten_all;
int finished_meal;
int is_dead;
t_info *data;
pthread_mutex_t *right;
pthread_mutex_t *left;
struct timeval last_dinner;
pthread_t thread;
} t_phil;
int count = 0;

这是一个函数,它将模拟晚餐,这个函数按预期工作,但我愿意接受可能的错误或改进。

void *routine(void *args)
{
t_phil *philo = (t_phil *)(args);
int i = philo->number;
if ((philo->number % 2))
sleep(1);
gettimeofday(&philo->last_dinner, 0);
while (!philo->is_dead)
{
pthread_mutex_lock(philo->left);
printf("Philosopher : %i has take left forkn", philo->number + 1);
pthread_mutex_lock(philo->right);
printf("Philosopher : %i has take right forkn", philo->number + 1);
gettimeofday(&philo->last_dinner, 0);
printf("Philosopher :%i is eating in at %lin", philo->number + 1, philo->last_dinner.tv_sec * 1000);
pthread_mutex_lock(&philo->data->t_mut);
// if (philo->data->num_of_dead_phil && !philo->data->min_dinner)
//     break;
if (philo->is_dead)
break;
gettimeofday(&philo->last_dinner, NULL);
philo->finished_meal++;
if (philo->finished_meal == philo->data->min_dinner)
{
philo->data->plate_eaten++;
philo->has_eaten_all = 1;
}
sleep(philo->data->time_to_eat);
pthread_mutex_unlock(&philo->data->t_mut);
pthread_mutex_unlock(philo->left);
pthread_mutex_unlock(philo->right);
if (philo->has_eaten_all)
break;
printf("Philosopher : %i is now sleeping at %lin", philo->number + 1, philo->last_dinner.tv_sec * 1000);
sleep(philo->data->time_to_sleep);
printf("Philosopher : %i is now thinking at %lin", philo->number + 1, philo->last_dinner.tv_sec * 1000);
}
return (NULL);
}

这一个函数没有按预期工作,我不知道为什么现在会发生这种情况,因为我的if语句似乎有正确的条件,但我从未进入if语句,这意味着该条件在应该满足的时候从未得到满足。我测试了很多值,每次都会出现相同的结果

void *watchers_phil(void *args)
{
t_info *data = (t_info *)args;
t_phil *phil = data->philo;
int i = 0;
struct timeval now;
while (1)
{
if (data->plate_eaten == data->num_of_phil)
break;
while (i < data->num_of_phil)
{
if ((phil[i].last_dinner.tv_sec) >= ((phil[i].last_dinner.tv_sec) + (long int)data->time_to_die))
{
gettimeofday(&now, NULL);
printf("Unfortunately Philosopher : %i, is dead because of starvation at %li....", phil[i].number, (now.tv_sec * 1000));
phil[i].is_dead = 1;
}
i++;
}
i = 0;
}
return (NULL);
}
int main(int argc, char *argv[])
{
t_info data;
pthread_t watchers;
memset(&data, 0, sizeof(t_info));
data.num_of_phil = atoi(argv[1]);
data.min_dinner = atoi(argv[2]);
data.time_to_eat = atoi(argv[3]);
data.time_to_die = atoi(argv[4]);
data.time_to_sleep = atoi(argv[5]);
t_phil *philo = malloc(sizeof(t_phil) * data.num_of_phil);
if (!philo)
return (1);
pthread_mutex_t *mutex = malloc(sizeof(pthread_mutex_t) * data.num_of_phil);
data.mutex = mutex;
if (!mutex)
{
free(philo);
return (1);
}
int i = 0;
while (i < data.num_of_phil)
{
pthread_mutex_init(&data.mutex[i], NULL);
i++;
}
printf("Number : %in", data.num_of_phil);
pthread_mutex_init(&data.t_mut, NULL);
i = 0;
while (i < data.num_of_phil)
{
philo[i].number = i;
philo[i].has_eaten_all = 0;
philo[i].data = &data;
philo[i].is_dead = 0;
philo[i].right = &data.mutex[i];
if (i == (data.num_of_phil - 1))
philo[i].left = &data.mutex[0];
else
philo[i].left = &data.mutex[i + 1];
i++;
}
data.philo = philo;
i = 0;
while (i < data.num_of_phil)
{
pthread_create(&data.philo[i].thread, NULL, routine, &data.philo[i]);
i++;
}
pthread_create(&watchers, NULL, watchers_phil, &data);
i = 0;
while (i < data.num_of_phil)
{
pthread_join(data.philo[i].thread, NULL);
i++;
}
pthread_join(watchers, NULL);
printf("Dinner eaten : %in", data.plate_eaten);
i = 0;
while (i < data.num_of_phil)
{
pthread_mutex_destroy(&data.mutex[i]);
i++;
}
pthread_mutex_destroy(&data.t_mut);
}

我推荐以下方法:

  • 有一个数组,其中包含每个哲学家线程的时间戳
  • 每个哲学家线程都会在进食开始或结束时更新各自的时间戳。为了确定自己的死亡,它也可以使用这个时间戳。你可以存储进食时间或饥饿发生的时间,我个人更愿意存储后者,因为它只需要计算一次饥饿时间(在其他地方你不需要进食时间(,而前者需要在每次饥饿测试中都这样做
  • 监视线程在所有这些时间戳上迭代,执行以下操作:
    • 确定线程是否已耗尽(当前时间-只需在循环前获取一次!-在存储时间戳之后,如果您遵循我上面的建议(。通过适当的方式将线程标记为饥饿,以便下次可以忽略它
    • 从非饥饿线程中记住最小值
  • 迭代后,睡眠时间与达到这个最短时间一样长(为了更好的精度,可能会更早地醒来(。如果一个哲学家更新了与这个最小值相对应的时间戳——没关系,监视器会徒劳地醒来,只是什么都不做,但除了消耗一点CPU时间之外,这不会有什么坏处

更新时间戳:您需要注意,除非您能够保证原子时间戳更新,否则您需要保护它们免受哲学家线程和监控线程之间的竞争条件的影响。

我看到两个选项:

  • 整个数组只有一个互斥对象。易于实现,但任何一个相关线程都可能阻塞另一个线程(如果两个哲学家都试图同时更新,则一个哲学家是监控器,或者监控器是一个或多个哲学家(。如果一个线程在持有该互斥对象时被抢占,则被阻塞的线程需要等待,直到该线程再次被调度。

  • 每个时间戳对应一个互斥对象。优点是,在监视器和一个哲学家之间可以只阻塞,而所有其他哲学家可以照常进行。然而,实现更为复杂,并且由于必须一次又一次地锁定和解锁互斥对象(涉及系统调用,这些调用相当昂贵(,监视器线程运行测试的速度会稍慢(不会注意到…(。

哲学家应该在读取检查自身饥饿的时间戳到写入更新的饥饿时间之间的整个时间内持有互斥。这确保了哲学家在吃饭时不会挨饿,尽管在这两者之间,你应该尽可能少做工作,尤其是如果你选择了一个互斥体(如果你无法避免,那就选择多个互斥体(。

最新更新