C / C ++ темы магическая разница в состоянии - PullRequest
0 голосов
/ 06 декабря 2018

Я хотел написать простое многопоточное приложение на C / C ++.Функция funProducent выдает 100 значений, и если случайно сгенерированное значение находится в заданном диапазоне, char добавляется в буфер.Функция funKonzument потребляет значения из буфера.Вот мой код:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define BUFFER_LIMIT 20

struct struktura{
    pthread_mutex_t mutex;
    pthread_cond_t bufferNotFull;
    pthread_cond_t bufferNotEmpty;
    int bufferIndex;
    char * buffer;
    int junk;
};

void * funProducent(void *arg){
    struktura * data = (struktura *) arg;
    int i = 0;
    while (i < 100) {
        pthread_mutex_lock(&data->mutex);
        if(data->bufferIndex == BUFFER_LIMIT - 1){
            pthread_cond_wait(&data->bufferNotFull, &data->mutex);
        }
        int randomValue = (rand() % 20) + 1;
        if( randomValue < 13 ){
            data->buffer[++data->bufferIndex] = 'a';
            printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
            pthread_cond_signal(&data->bufferNotEmpty);
        } else {
            data->junk++;
        }
        pthread_mutex_unlock(&data->mutex);
        i++;
    }
    printf("producent is done\n");
}

void * funKonzument(void *arg){
    struktura * data = (struktura *) arg;
    int i = 0;
    while (i + data->junk < 100) {  
        printf("%d\n", i + data->junk);
        pthread_mutex_lock(&data->mutex);
        if(data->bufferIndex < 0){
            pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);
        }
        printf("%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
        data->bufferIndex--;
        pthread_cond_signal(&data->bufferNotFull);
        pthread_mutex_unlock(&data->mutex);
        i++;
    }
    printf("konzument is done\n");
}

int main(int argc, char** argv) {

pthread_t threadProducent, threadKonzument;
struktura threadData;
threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
threadData.bufferIndex = -1;
threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
threadData.mutex = PTHREAD_MUTEX_INITIALIZER;
threadData.junk = 0;

pthread_create(&threadProducent, NULL, funProducent, &threadData);
pthread_create(&threadKonzument, NULL, funKonzument, &threadData);

pthread_join(threadProducent, NULL);
pthread_join(threadKonzument, NULL);

free(threadData.buffer);
pthread_mutex_destroy(&threadData.mutex);
pthread_cond_destroy(&threadData.bufferNotFull);
pthread_cond_destroy(&threadData.bufferNotEmpty);
return 0;
}

Когда я пытаюсь запустить этот код, иногда он застревает в funKonzument в этой строке:

pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);

Но ... когда я меняю условие в funProducentметод от:

if( randomValue < 13 )

до

if( randomValue > 8 )

все работает отлично.Кто-нибудь может объяснить мне, в чем волшебная разница между этими двумя условиями?

1 Ответ

0 голосов
/ 06 декабря 2018

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

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdexcept>
#include <functional>

#define BUFFER_LIMIT 20

struct struktura{
    pthread_mutex_t mutex;
    pthread_cond_t bufferNotFull;
    pthread_cond_t bufferNotEmpty;
    int bufferIndex;
    char * buffer;
};

// a lock context manager
class mlock {
    pthread_mutex_t* mtx;
public:
    mlock(pthread_mutex_t& Mtx) :
        mtx(&Mtx)
    {
        int rv=pthread_mutex_lock(mtx);
        if(rv) throw std::runtime_error(std::to_string(rv));
    }
    mlock(const mlock&) = delete;
    mlock(mlock&&) = delete;
    mlock& operator=(const mlock&) = delete;
    mlock& operator=(mlock&&) = delete;
    ~mlock() {
        pthread_mutex_unlock(mtx);
    }
};

// silly loop to take care of spurious wakes
void cwait(pthread_cond_t& c, pthread_mutex_t& m, std::function<bool()> f) {
    while(f()) pthread_cond_wait(&c, &m);
}

void* funProducent(void *arg){
    struktura* data = static_cast<struktura*>(arg);
    int i = 0;
    while(i < 100) {
        mlock dummy(data->mutex);
        cwait(data->bufferNotFull, data->mutex, [&](){return data->bufferIndex == BUFFER_LIMIT - 1;});

        int randomValue = (rand() % 20) + 1;
        if( randomValue < 13 ){
            data->buffer[++data->bufferIndex] = 'a';
            printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
            i++;
            pthread_cond_signal(&data->bufferNotEmpty);
        }
    }
    printf("producent is done\n");
    return nullptr;
}

void* funKonzument(void *arg){
    struktura* data = static_cast<struktura*>(arg);
    int i = 0;
    while(i < 100) {
        mlock dummy(data->mutex);
        cwait(data->bufferNotEmpty, data->mutex, [&](){return data->bufferIndex<0;});

        printf("\t\t\t%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
        data->bufferIndex--;
        i++;
        pthread_cond_signal(&data->bufferNotFull);
    }
    printf("\t\t\tkonzument is done\n");
    return nullptr;
}

int main() {
    pthread_t threadProducent, threadKonzument;
    struktura threadData;
    threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
    threadData.bufferIndex = -1;
    threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
    threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
    threadData.mutex = PTHREAD_MUTEX_INITIALIZER;

    pthread_create(&threadProducent, NULL, funProducent, &threadData);
    pthread_create(&threadKonzument, NULL, funKonzument, &threadData);

    pthread_join(threadProducent, NULL);
    pthread_join(threadKonzument, NULL);

    free(threadData.buffer);
    pthread_mutex_destroy(&threadData.mutex);
    pthread_cond_destroy(&threadData.bufferNotFull);
    pthread_cond_destroy(&threadData.bufferNotEmpty);
    return 0;
}
...