Почему эта многопоточная программа выдает ошибку Segfault?

Я играю с решением проблемы обедающих философов с использованием мьютексов, однако программа segfaulting, вероятно, связана с ошибкой, связанной с потоком. То, что я пытаюсь сделать здесь, это в основном думать о форке как о мьютексе и создать функцию void *eat(void *arg) , а затем закрыть критическую секцию (критическая секция - это просто поток, объявляющий свой идентификатор и который в настоящее время ест), независимо от того, какая функция вызывается, затем я перебираю все мои философы и проверяю, делится ли его идентификатор (идентификатор начинается с 0) на 2.
В первом раунде едят только идентификаторы потоков, которые делятся на 2, а во втором раунде едят только идентификаторы потоков, которые не делятся, и так далее в бесконечном цикле.
Я знаю, что это до глупости простое решение, которое, вероятно, не решает проблему. в первую очередь. так что, пожалуйста, потерпите меня. если у вас есть какие-либо вопросы, пожалуйста, дайте мне знать в комментарии.

struct typedef t_philo
{
    pthread_t thread;
    pthread_mutex_t fork;
    int id;
}
t_philo;

void *eat(void *arg)
{
    t_philo *philo = (t_philo *)arg;

    pthread_mutex_lock(&philo->fork);
    printf("philo with id: %i is eating\n", philo->id);
    pthread_mutex_unlock(&philo->fork);
    return (NULL);
}

void first_round(t_philo *philo, int len)
{
    for (int i = 0; i < len; i++)
        if (!(i % 2))
            pthread_join(philo[i].thread, NULL);
}

void second_round(t_philo *philo, int len)
{
    for (int i = 0; i < len; i++)
        if ((i % 2))
            pthread_join(philo[i].thread, NULL);
}


int main(int argc, char **argv)
{
    t_philo *philo;
    // how many philosophers is given as first arg.
    int len = atoi(argv[1]);
    if (argc < 2)
        exit(EXIT_FAILURE);
    philo = malloc(sizeof(*philo) * atoi(argv[1]));
    //this function add id's and initialize some data.
    init_data(philo, argv);
    for (int i = 0; i < len; i++)
        pthread_create(&philo[i].thread, NULL, eat(&philo[i]), &philo[i]);

        while (1)
        {
            first_round(philo, len);
            second_round(philo, len);
        }
    return 0;
}

ВЫХОД

philo with id: 0 is eating
philo with id: 1 is eating
philo with id: 2 is eating
philo with id: 3 is eating
philo with id: 4 is eating
            .
            .
            .
            .
philo with id random is eating
[1]    29903 segmentation fault

Вывод каждый раз достигает случайного идентификатора и segfault, поэтому я пришел к выводу, что это может быть ошибка потока.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
31
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В mainint len = argv[1]; неправильно. Если вы скомпилировали с включенными предупреждениями (например, -Wall), этот оператор будет помечен компилятором.

Как бы то ни было, вы получаете значение огромный для len. Итак, позже цикл for переполнит выделенный вами массив, и у вас будет UB.

Вы, вероятно, хотите: int len = atoi(argv[1]); как у вас для malloc.

И вы хотите сделать эту после проверку argc.

И зачем вызывать atoi дважды [с исправлением]?

Вот рефакторинг кода:

int
main(int argc, char **argv)
{
    t_philo *philo;

    if (argc < 2)
        exit(EXIT_FAILURE);

    // how many philosophers is given as first arg.
    int len = atoi(argv[1]);

    philo = malloc(sizeof(*philo) * len);

    // do stuff ...

    return 0;
}

извините, это не проблема, я просто минимизировал свою программу для простоты, в моей длинной версии я фактически преобразовываю длину и другие 3 параметра в целые числа и сохраняю их более или менее эффективным способом, это всего лишь короткая версия программа, которая проливает свет на логику, которая имеет значение, проблема связана с ошибкой, программа работает и segfaulting случайным образом с разными идентификаторами. Я исправлю ошибку, извините за введение в заблуждение.

home 17.03.2022 20:44

Пожалуйста, редактировать ваш вопрос и добавьте минимальную версию, которая компилирует чисто и может вызвать проблему

Craig Estey 17.03.2022 20:49
Ответ принят как подходящий

pthread_create имеет следующий прототип:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

start_routine это указатель функции. Однако вы звоните pthread_create с помощью неверные аргументы: eat(&philo[i]). Таким образом, программа вызывает eat правильно, а затем пытается звоните NULL (из нового потока), потому что это значение, возвращаемое eat. Случайность возникает из-за переменного времени, необходимого для фактического создания потоков.

Обратите внимание, что с помощью отладчика должен помочь вам очень легко найти и устранить проблему. Отладчики, такие как gdb, немного сложны в освоении, но как только это сделано, такие ошибки, как segfault, становится почти легко исправить. Я также удивлен, что такой компилятор, как clang, не заметит проблемы с набором текста во время компиляции.

Да, пропустил это, мне нужно пересмотреть свои основы, спасибо. если у вас есть хорошие ресурсы для изучения отладчика, пожалуйста, облегчите.

home 17.03.2022 20:57

Существуют документы/веб-сайты, такие как Вот этот или Вот этот, но большинство из них не очень удобны для пользователя. Я лично узнал на работе (в течение нескольких лет). При этом я думаю, что в этом случае не должно быть слишком сложно использовать gdb для поиска ошибки: достаточно просто скомпилировать с помощью -g, запустить программу с помощью gdb и просмотреть стек. Обратите внимание, что gdb имеет очень полезный режим --tui для графического интерфейса на основе консоли.

Jérôme Richard 17.03.2022 23:08

Другие вопросы по теме