我正在做一个解决哲学家用餐问题的程序,当我试图锁定我的互斥锁时,我得到了一个分段错误。我不允许使用全局变量,所以我不得不用指针移动我的互斥锁,我觉得我这样做的方式很糟糕,我有点迷失在自己的代码中。下面是重要的函数
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <pthread.h>
# include <sys/time.h>
typedef struct s_philo
{
int n_philo; //* number of philosophers
int time_to_die; //* time to die in ms
int time_to_eat; //* time to eat in ms
int time_to_sleep; //* time to sleep in ms
int need_eat; //* number of times each philosopher must eat
u_int64_t start_time; //* time when the simulation starts
} t_philo;
typedef struct s_data
{
pthread_mutex_t *left_fork; //* pointer to the left fork
pthread_mutex_t *right_fork; //* pointer to the right fork
u_int64_t last_eaten; //* time when the philosopher last ate
t_philo *philo; //* links to the philo struct
int id; //* id of the philosopher
int dead; //* 1 if the philosopher is dead
int is_eating; //* 1 if the philosopher is eating
int n_eat; //* number of times the philosopher has eaten
} t_data;
u_int64_t get_time(void); //* returns the time in ms
int ft_usleep(useconds_t time); //* sleeps for time ms
int ft_atoi(const char *str); //* converts a string to an int
void init_philo(t_philo *philo, int argc, char **argv); //* initializes the philo struct
void init_data(t_data *data, t_philo *philo, int id); //* initializes the data struct
void *routine(void *vdata); //* routine of the philosophers
void *monitor(void *vdata); //* monitors the philosophers (exemples: death)
int ft_eat(void *vdata); //* eat function
int ft_sleep(void *vdata); //* sleep function
int ft_think(void *vdata); //* think function
int main(int argc, char **argv)
{
t_philo philo;
t_data data[ft_atoi(argv[1])];
pthread_t philos[ft_atoi(argv[1])];
pthread_t monitoring;
pthread_mutex_t forks[ft_atoi(argv[1])];
int i;
i = 0;
init_philo(&philo, argc, argv);
while (i < philo.n_philo)
{
pthread_mutex_init(&forks[i], NULL);
i++;
}
i = 0;
while(i < philo.n_philo)
{
init_data(&data[i], &philo, i);
data->left_fork = &forks[i];
if (i == 0)
data->right_fork = &forks[philo.n_philo - 1];
else
data->right_fork = &forks[i - 1];
i++;
}
i = 0;
philo.start_time = get_time();
pthread_create(&monitoring, NULL, &monitor, &data);
while (i < philo.n_philo)
{
pthread_create(&philos[i], NULL, &routine, &data[i]);
i++;
}
i = 0;
while (i < philo.n_philo)
{
pthread_join(philos[i], NULL);
pthread_mutex_destroy(&forks[i]);
i++;
}
pthread_join(monitoring, NULL);
return (0);
}
void *monitor(void *vdata)
{
t_data *data;
int i;
data = (t_data *)vdata;
i = 0;
while (1)
{
while(i < data->philo->n_philo)
{
if (get_time() - data[i].last_eaten > (u_int64_t)data[i].philo->time_to_die && data[i].last_eaten != 0 && data[i].is_eating == 0)
{
printf("%llu %d died\n", get_time() - data->philo->start_time, data->id);
data[i].dead = 1;
exit(0);
}
i++;
}
i = 0;
}
return (NULL);
}
void *routine(void *vdata)
{
t_data *data;
data = (t_data *)vdata;
if (data->id % 2 == 0)
ft_sleep(data);
while (data->dead == 0)
{
ft_eat(data);
ft_sleep(data);
ft_think(data);
}
return (NULL);
}
int ft_sleep(void *vdata)
{
time_t strt;
t_data *data;
data = (t_data *)vdata;
strt = data->philo->start_time;
printf("%llu %d is sleeping\n", get_time() - strt, data->id);
ft_usleep(data->philo->time_to_sleep);
return (0);
}
int ft_eat(void *vdata)
{
time_t strt;
t_data *data;
data = (t_data *)vdata;
strt = data->philo->start_time;
pthread_mutex_lock(data->left_fork);
printf("%llu %d has taken a fork\n", get_time() - strt, data->id);
pthread_mutex_lock(data->right_fork);
printf("%llu %d has taken a fork\n", get_time() - strt, data->id);
data->is_eating = 1;
printf("%llu %d is eating\n", get_time() - strt, data->id);
ft_usleep(data->philo->time_to_eat);
data->last_eaten = get_time();
data->n_eat++;
pthread_mutex_unlock(data->left_fork);
pthread_mutex_unlock(data->right_fork);
data->is_eating = 0;
return (0);
}
int ft_think(void *vdata)
{
time_t strt;
t_data *data;
data = (t_data *)vdata;
strt = data->philo->start_time;
printf("%llu %d is thinking\n", get_time() - strt, data->id);
return (0);
}
int ft_usleep(useconds_t time)
{
time_t start;
start = get_time();
while ((get_time() - start) < time)
usleep(time / 10);
return(0);
}
u_int64_t get_time(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((tv.tv_sec * (u_int64_t)1000) + (tv.tv_usec / 1000));
}
void init_philo(t_philo *philo, int argc, char **argv)
{
philo->n_philo = ft_atoi(argv[1]);
philo->time_to_die = ft_atoi(argv[2]);
philo->time_to_eat = ft_atoi(argv[3]);
philo->time_to_sleep = ft_atoi(argv[4]);
if (argc == 6)
philo->need_eat = ft_atoi(argv[5]);
else
philo->need_eat = -1;
philo->start_time = get_time();
}
void init_data(t_data *data, t_philo *philo, int id)
{
data->id = id;
data->last_eaten = 0;
data->dead = 0;
data->is_eating = 0;
data->n_eat = 0;
data->philo = philo;
}
2条答案
按热度按时间wj8zmpe11#
根据这个定义:
,这不会初始化所有数据:
变量
data
指定了一个数组,所以它在上面的表达式中出现的地方,它衰减到指向第一个数组元素的指针。也就是说,你反复设置data[0]
的分支,而从不设置其他元素的分支。i
没有出现在表达式data
中,并且data
本身从未被修改,这将是你的线索。应该这样写:
有了这个改变,你的程序就可以在没有segfaulting的情况下为我运行,并最终终止。
ru9i0ody2#
这不是答案。我懒得调试你的代码。这只是一些扩展形式的注解。
你创建了一个新的线程来运行
monitor()
,但是你的main()
线程除了在monitor
线程运行时等待之外什么也不做。为什么要创建线程呢?为什么不直接从主线程调用monitor()
呢?评论中的信息太多:例如,
int time_to_die; //*time to die in ms
。您可以使用int time_to_die_ms;
传达相同的信息t_philo
的更好的名称是t_parameters
或t_configuration
。t_data
的更好的名称是t_philosopher
。routine()
的更好的名称是philosopher_main()
。所有的
void*
参数和所有的类型转换是怎么回事?唯一需要 *void*
的地方是线程函数monitor
和routine
的参数。任何其他地方,如果你想传递一个指向t_data
示例的指针,参数应该声明为t_data*
类型。例如,ft_eat(t_data* data)
而不是ft_eat(void* vdata)
。monitor()
线程和routine()
线程共享对t_data
字段的访问,而没有任何同步。这使得程序的行为正式“未定义”。monitor()
线程 spins: 它 * 不断地 * 尽可能快地检查哲学家。这是使用整个CPU的所有可用周期,同时可能 * 饥饿 * 其他线程。您不需要它经常检查。也许在外部循环中睡眠一秒钟,给予其他线程有机会做他们的事情。在
ft_usleep()
中,毫秒和微秒之间存在很多混淆。看起来每次调用它,它都会执行大约一万次循环迭代,休眠time/10000
毫秒。(time/10
微秒)。如果你喜欢毫秒,那么除了在调用usleep()
时 * 小心地、明确地 ,在任何地方都使用毫秒。另外,我会去掉循环:你已经得到了声明为返回
int
的函数,它总是返回return (0)
,而它们的调用者从不检查返回值。为什么这些函数不声明为返回void
呢?这没有任何意义:
如果你做的下一件事是杀死整个程序,那么设置哲学家的
dead
标志就没有意义了。最好干脆把dead
标志都去掉。如果哲学家因为死锁而“死”了,那么即使monitor()
没有立即杀死整个程序,它也永远不会醒来检查这个标志。