我正在制作一个解决用餐哲学家问题的程序,当我试图锁定我的互斥锁时,我得到了一个分割错误。我不允许使用全局变量,所以我不得不用指针移动我的互斥锁,我觉得我这样做的方式是相当janky,我有点迷失在我自己的代码。下面是重要的函数
# 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 diedn", 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 sleepingn", 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 forkn", get_time() - strt, data->id);
pthread_mutex_lock(data->right_fork);
printf("%llu %d has taken a forkn", get_time() - strt, data->id);
data->is_eating = 1;
printf("%llu %d is eatingn", 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 thinkingn", 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;
}
给出这个定义:
t_data data[ft_atoi(argv[1])];
,这不会初始化所有的数据:
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++; }
变量data
指定了一个数组,因此它在上面的表达式中出现的地方,衰减为指向第一个数组元素的指针。也就是说,您反复设置data[0]
的分支,而从不设置其他元素的分支。i
没有出现在表达式data
中,data
本身也没有被修改过,这就是你的线索。
应该这样写:
for (i = 0; i < philo.n_philo; i++) { // prefer 'for' for simple iteration
init_data(&data[i], &philo, i);
data[i].left_fork = &forks[i];
if (i == 0) { // never omit braces around 'if' and 'else' bodies
data[i].right_fork = &forks[philo.n_philo - 1];
} else {
data[i].right_fork = &forks[i - 1];
}
}
有了这个改变,你的程序为我运行而没有分段故障,并最终终止。
这不是答案。我懒得调试你的代码。这只是一些扩展形式的注释。
您创建了一个新线程来运行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()
线程旋转:它不断地尽可能快地检查哲学家。这使用了整个CPU的所有可用周期,同时可能饿死其他线程。你不需要经常检查。也许在外循环中休眠一秒钟,让其他线程有机会做他们的事情。
在ft_usleep()
中关于毫秒和微秒的混淆。看起来每次调用它时,它将执行大约10,000次循环迭代,在每次迭代中休眠time/10000
毫秒(time/10
微秒)。如果您喜欢毫秒,那么除了在调用usleep()
时小心且显式地之外,所有地方都使用毫秒。另外,我还去掉了循环:*
void ft_mssleep(int milliseconds) {
useconds_t microseconds = (useconds_t)1000 * milliseconds;
usleep(microseconds);
}
你有函数声明返回int
总是return (0)
,他们的调用者从不检查返回值。为什么这些函数没有声明为返回void
?
这没有任何意义:
data[i].dead = 1;
exit(0);
设置哲学家的dead
标志是没有意义的,如果你做的下一件事是杀死整个程序。不妨干脆把dead
旗全部去掉。如果一个哲学家"死了";因为它是死锁的,所以即使monitor()
没有而立即杀死整个程序,它也永远不会唤醒来检查标志。
*循环是因为您担心调用者想要睡眠超过几千秒时会溢出吗?这可能是对这个例子的需求考虑过多了,但是如果你真的想要处理这种情况,那么你可能会想做一些比你实际做的更复杂的事情。