Проблема «читатели-писатели» «писатели-предпочтения» (читатели могут умереть с голоду)

У меня проблема с читателем-писателем. Я хочу написать, что авторы предпочитают решение с использованием мьютекса. Пока я написал это

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <memory.h>
#include <stdbool.h>
#include <stdint.h>
#include<unistd.h>

int NO_READERS;
int NO_WRITERS;
int NO_READERS_READING = 0;     // How many readers need shared resources
int NO_WRITERS_WRITING = 0;     // How many writers need shared resources

pthread_mutex_t resourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t tryResourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t readerMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t writerMutex = PTHREAD_MUTEX_INITIALIZER;

void *readerJob(void *arg) {
    int *id = (int*)arg;
    while (1) {
        pthread_mutex_lock(&tryResourceMutex); // Indicate reader is trying to enter
        pthread_mutex_lock(&readerMutex);
        NO_READERS_READING++; // Indicate that you are needing the shared resource (one more reader)
        if (NO_READERS_READING == 1) {
            pthread_mutex_lock(&resourceMutex);
        }
        pthread_mutex_unlock(&readerMutex);
        pthread_mutex_unlock(&tryResourceMutex);
         printf("READER ID %d WALKED IN \n",*id);
        printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
                NO_READERS - NO_READERS_READING,
                NO_WRITERS - NO_WRITERS_WRITING,
                NO_READERS_READING,
                NO_WRITERS_WRITING);
        sleep(1);
        pthread_mutex_lock(&readerMutex);
        NO_READERS_READING--;
        if (NO_READERS_READING == 0) { // Check if you are the last reader
            pthread_mutex_unlock(&resourceMutex);
        }
        pthread_mutex_unlock(&readerMutex);
    }
    return 0;
}

void *writerJob(void *arg) {
    int *id = (int*)arg;
    while (1) {
        pthread_mutex_lock(&writerMutex);
        NO_WRITERS_WRITING++;
        if (NO_WRITERS_WRITING == 1) {
            pthread_mutex_lock(&tryResourceMutex); // If there are no other writers lock the readers out
        }
        pthread_mutex_unlock(&writerMutex);

        pthread_mutex_lock(&resourceMutex);
        printf("WRITER ID %d WALKED IN \n",*id);
        printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
                NO_READERS - NO_READERS_READING,
                NO_WRITERS - NO_WRITERS_WRITING,
                NO_READERS_READING,
                NO_WRITERS_WRITING);
        sleep(1);
        pthread_mutex_unlock(&resourceMutex);

        pthread_mutex_lock(&writerMutex);
        NO_WRITERS_WRITING--;
        if (NO_WRITERS_WRITING == 0) {
            pthread_mutex_unlock(&tryResourceMutex); // If there are no writers left unlock the readers
        }
        pthread_mutex_unlock(&writerMutex);
    }
    return 0;
}

int main(int argc, char *argv[]) {
    NO_READERS = atoi(argv[1]);
    NO_WRITERS = atoi(argv[2]);

    // Initialize arrays of threads IDs
    pthread_t *readersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));
    pthread_t *writersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));

    // Initialize shared memory (array) with random numbers

    // Create readers threads
    for (int i = 0; i < NO_READERS; ++i) {
        int* id = (int*)(malloc(sizeof(int)));
        *id = i;
        pthread_create(&readersThreadsIds[i], NULL, readerJob,(void*)id);
    }
    // Create writers threads
    for (int i = 0; i < NO_WRITERS; ++i) {
        int* id = (int*)(malloc(sizeof(int)));
        *id = i;
        pthread_create(&writersThreadsIds[i], NULL, writerJob, (void*)id);

    }

    // Wait for readers to finish
    for (int i = 0; i < NO_READERS; ++i) {
        pthread_join(readersThreadsIds[i], NULL);
        }
    // Wait for writers to finish
    for (int i = 0; i < NO_WRITERS; ++i) {
        pthread_join(writersThreadsIds[i], NULL);
    }

    free(readersThreadsIds);
    free(writersThreadsIds);
    pthread_mutex_destroy(&resourceMutex);
    pthread_mutex_destroy(&tryResourceMutex);
    pthread_mutex_destroy(&readerMutex);
    pthread_mutex_destroy(&writerMutex);
    return 0;
}

И я не уверен, что это должно так работать. Может ли кто-нибудь проверить это для меня? Я хочу иметь информацию о том, какой читатель или писатель входит или выходит. Кажется, что он застрял в какой-то момент, но я не знаю, почему.


person kubek_tysionc    schedule 11.06.2019    source источник
comment
В конце вы как бы говорите, что программа работает не так, как вы ожидаете. Было бы уместно представить результаты работы программы или другую информацию, позволяющую сделать такой вывод, и пояснить свой анализ.   -  person John Bollinger    schedule 11.06.2019
comment
Вначале приходят все читатели, а затем и писатели, что невозможно одновременно. Тогда все читатели ушли, но в библиотеке одновременно находится более одного писателя. Лучшая идея — запустить его и убедиться в этом.   -  person kubek_tysionc    schedule 11.06.2019


Ответы (2)


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

Чтобы увидеть, как он работает, как задумано, добавьте задержку в начале цикла while каждого потока:

usleep((rand() % 10000) * 10000);

Это позволит читателям периодически получать доступ, когда все писатели находятся в usleep().

person mevets    schedule 11.06.2019

В начале все читатели входят,

Под «входом» я понимаю, что вы имеете в виду выполнение вызовов printf() в цикле readerJob. Неудивительно, что все читатели приходят первыми, потому что вы запускаете их первыми, и в вероятном случае, если первый поток чтения, пытающийся заблокировать tryResourceMutex, сделает это до того, как это сделает любой поток записи, он затем также заблокирует resourceMutex(), предотвращая любой писатель из "Входя". Но это не мешает модулям записи увеличивать NO_WRITERS_WRITING. И это также не мешает одному из них блокировать tryResourceMutex и удерживать его заблокированным.

Затем вызов sleep() в считывателе (вероятно) приведет к тому, что resourceMutex будет удерживаться непрерывно достаточно долго, чтобы все считыватели вошли раньше, чем кто-либо из записывающих, поскольку каждый пишущий должен получить resourceMutex, чтобы войти.

затем также писатели, что не должно быть возможным одновременно.

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

Когда читатели уходят, они блокируются от повторного входа сразу же, потому что один из писателей держит tryResourceMutex. В конце концов, последний читатель выйдет и освободит файл resourceMutex. Это позволит модулям записи работать по одному, но если вызов sleep() расположен там, где он находится в цикле записи, крайне маловероятно, что число модулей записи когда-либо упадет до нуля, чтобы кто-либо из модулей чтения мог перезагрузиться. войти. Однако если бы это произошло, то, скорее всего, повторился бы тот же цикл: все читатели вошли бы один раз, а все писатели выстроились бы в очередь.

Тогда все читатели ушли, но в библиотеке одновременно находится более одного писателя.

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

Итого: вы сами себя запутали. Вы используете переменную NO_WRITERS_WRITING в первую очередь для представления количества писателей, готовых писать, но ваш обмен сообщениями использует ее так, как если бы это было число, которое действительно пишет. То же самое не относится к NO_READERS_READING, потому что, как только поток получает мьютекс, необходимый для изменения этой переменной, ничто другое не мешает ему продолжить работу в комнате.

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

person John Bollinger    schedule 11.06.2019
comment
Да, я вижу, что я сделал неправильно с NO_WRITERS_WRITING. Я добавил еще одну переменную для подсчета писателей. Но я все еще не могу смоделировать это правильно. Может быть, потому что я не знаю, как должен выглядеть правильный результат. У меня проблема с размещением этих снов. pastebin.com/snB5XhT8 Теперь правильно? - person kubek_tysionc; 11.06.2019
comment
@kubek_tysionc, если модуль записи sleep()s между увеличением NO_WRITERS_WRITING и повторным уменьшением, особенно если он делает это, удерживая resourceMutex, то это имитирует процесс записи, которому требуется некоторое время для выполнения своей задачи. Это нормально, но если читатели когда-нибудь будут работать, то должны быть времена, когда писатели не работают. Это лучше всего реализовать с помощью второго sleep — более длинного, желательно случайного — либо в самом начале, либо в самом конце цикла записи. - person John Bollinger; 11.06.2019