Проблема с синхронизацией потоков, использующих условные переменные для вывода Мандельброта

Я пытаюсь синхронизировать потоки с переменными состояния для вывода мандельброта, но получаю неправильный мандельборт. Функции output_mandel_line и compute_mandel_line заданы и являются правильными. я сделал void *function() и int main(). Я подозреваю, что проблема связана с оператором if внутри mutex_lock. Я попробовал другой оператор if или while, но не смог это исправить. Возможно, есть проблема и в том, как я использую cond_wait,cond_broadcast.


int nthreads;
pthread_cond_t cond;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

/***************************
 * Compile-time parameters *
 ***************************/

/*
 * Output at the terminal is is x_chars wide by y_chars long
 */
int y_chars = 50;
int x_chars = 90;

/*
 * The part of the complex plane to be drawn:
 * upper left corner is (xmin, ymax), lower right corner is (xmax, ymin)
 */
double xmin = -1.8,
    xmax = 1.0;
double ymin = -1.0,
    ymax = 1.0;

/*
 * Every character in the final output is
 * xstep x ystep units wide on the complex plane.
 */
double xstep;
double ystep;

/*
 * This function computes a line of output
 * as an array of x_char color values.
 */
void
compute_mandel_line(int line, int color_val[])
{
}

/*
 * This function outputs an array of x_char color values
 * to a 256-color xterm.
 */
void
output_mandel_line(int fd, int color_val[])
{
}

    /* Now that the line is done, output a newline character */
    if (write(fd, &newline, 1) != 1) {
        perror("compute_and_output_mandel_line: write newline");
        exit(1);
    }
}

void *
function(void *arg)
{
    int *number = (int *) arg,
        line;

    int color_val[x_chars];

    for (line = *number; line < y_chars; line += nthreads) {
        compute_mandel_line(line, color_val);
        pthread_mutex_lock(&mutex);

        output_mandel_line(1, color_val);
        pthread_cond_signal(&cond);
        if (line + nthreads < y_chars) {
            pthread_cond_wait(&cond, &mutex);
        }

        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);

    }
    free(arg);

    return NULL;
}

int
main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "Wrong format");
        exit(1);
    }
    nthreads = atoi(argv[1]);
    int i;
    pthread_t t[nthreads];

    // pthread_t *t =(pthread_t*)malloc(nthreads * sizeof(pthread_t));

    // ret =(pthread_cond_t*)malloc(nthreads * sizeof(pthread_cond_t));
    int ret;

    xstep = (xmax - xmin) / x_chars;
    ystep = (ymax - ymin) / y_chars;

    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);

    for (i = 0; i < nthreads; i++) {
        int *a = malloc(sizeof(int));

        *a = i;
        ret = pthread_create(&t[i], NULL, function, a);
        if (ret) {
            perror_pthread(ret, "error");
            exit(1);
        }
    }

    for (i = 0; i < nthreads; i++) {
        ret = pthread_join(t[i], NULL);
        if (ret) {
            perror_pthread(ret, "error");
            exit(1);
        }
    }

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);

    /*
     * Allocate memory for threads and semaphores
     * draw the Mandelbrot Set, one line at a time.
     * Output is sent to file descriptor '1', i.e., standard output.
     */

    reset_xterm_color(1);
    return 0;
}

Я попытался изменить оператор if и порядок условных переменных cond_wait, cond_signal.

У вас состояние гонки. Я думаю, вам нужны две переменные условия. Когда вы делаете pthread_cond_signal, удерживая блокировку, и сразу же делаете pthread_cond_wait с той же переменной условия, получатель никогда не будет освобожден, потому что отправитель получит сигнал. И вам нужно зациклить доступные условия: pthread_mutex_lock(&mutex); while (line + nthreads < y_chars) pthread_cond_wait(&cond,&mutex); pthread_mutex_unlock(&mutex); И я не совсем уверен, но вам, возможно, придется освободить мьютекс перед подачей сигнала.

Craig Estey 15.05.2024 23:08

Первый вопрос, на который нужно ответить, когда вы выполняете ожидание условной переменной, — какое условие вы ожидаете? Вы можете использовать одно и то же CV в нескольких потоках или несколько раз, чтобы дождаться, пока различные условия станут истинными, но если нет выражения, которое вы можете проверить, чтобы определить, стоит ли ждать (или ждать больше), тогда вы используете свое CV. неправильно.

John Bollinger 16.05.2024 21:39

А поскольку вам нужно проверить условие после того, как вы вернетесь из ожидания, и, возможно, подождать еще немного, идиомы для правильного использования CV в основном выполняют ожидание внутри цикла.

John Bollinger 16.05.2024 21:41

@CraigEstey, сигналы CV не ставятся в очередь и не сохраняются, ни один. (Они не являются «сигналами» в смысле функции kill().) Только поток, уже заблокированный в pthread_cond_wait(), может быть разбужен вызовом pthread_cond_signal(). Ни один поток не может вырваться из своего состояния pthread_cond_wait(), независимо от того, какой мьютекс он удерживает и когда.

John Bollinger 16.05.2024 21:50
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Но если вы хотите использовать резюме, важно понимать модель использования:

  • Цель CV — позволить потоку при необходимости приостановить работу до тех пор, пока какое-либо условие не станет истинным в результате действия другого потока. Если нет условий для проверки потока, чтобы определить, стоит ли продолжать, то CV — неправильный инструмент для этой работы.

  • Поток, достигающий точки ожидания, опосредованной CV, должен сначала проверить, истинно ли условие для его продолжения. Если да, то вообще не ждет.

  • Если поток выполняет ожидание CV, то после разблокировки он должен еще раз проверить, удовлетворено ли условие (a) для продолжения работы. Это предполагает несколько возможностей:

    • что CV было сообщено по какой-то другой причине, кроме конкретного условия, изменения которого ожидал поток

    • что условие, хотя, возможно, и было истинным во время сигнала CV, снова становится ложным к тому времени, когда поток получает возможность запуститься

    • что поток проснулся внезапно, без отправки сигнала в CV вообще.

    Обычно ожидающий поток возвращается в режим ожидания, если его условия продолжения не выполняются.

  • Не случайно CV используются вместе с мьютексами. Поток, ожидающий CV, должен наблюдать за влиянием одного или нескольких других потоков на некоторую часть (общих) данных, которые делают условие истинным, которое ранее было ложным. Чтобы избежать гонки данных вокруг этих данных, требуется синхронизация, которая (если все сделано правильно) обеспечивается мьютексом.


Очевидно, вы хотите, чтобы ваши потоки работали по очереди и записывали свои выходные данные в циклическом порядке. В этом случае условием, которого хочет дождаться каждый поток, будет, по-английски, «это моя очередь». Чтобы поток мог оценить это вычислительно, должна быть общая переменная, которая определяет, чья очередь. Может быть, что-то вроде этого:

static int whose_turn = 0;

Тогда ваш цикл ожидания может выглядеть так:

        pthread_mutex_lock(&mutex);

        while (whose_turn != *number) { // check the condition
            // it's not my turn yet
            pthread_cond_wait(&cond, &mutex);
        }

        // do my work
        output_mandel_line(1, color_val);
        
        // now it's the next thread's turn
        whose_turn = (*number + 1) % nthreads;

        pthread_mutex_unlock(&mutex);

        // wake _all_ the threads, to ensure that the one whose turn it is
        // is wakened
        pthread_cond_broadcast(&cond);

Дополнительные замечания:

  • Вам следует свести к минимуму работу, выполняемую в критической области, которую в основном выполняет output_mandel_line(). Я не уверен, что делает ваша функция set_xterm_color(), но если можно переместить все вызовы этой функции за пределы критической области, это улучшит вашу производительность. Не в последнюю очередь благодаря тому, что вы можете отправить всю линию одним write() вызовом.

  • Для ясности я опустил проверки возвращаемого значения в приведенном выше коде, но вам следует проверять возвращаемое значение каждого вызова функции, которая использует возвращаемое значение для сигнализации об ошибках. За исключением, возможно, случаев, когда вас на самом деле не волнуют ошибки.

Спасибо за решение и все объяснение!!!

soa008 17.05.2024 09:43

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