Linux Проблема производителя и потребителя pthread в c - почему мой код не выводит никаких результатов? - PullRequest
0 голосов
/ 12 июля 2020

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

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

Я новичок в pthreads и sempahores, поэтому у меня что-то не так.

Может ли кто-нибудь указать мне в правильном направлении?

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

#define BUFFER_SIZE 2
#define PRODUCER_SIZE 2

//string
char message[] = "Now is the time for all good people to come to the aid of their country";
int msgindex = 0;

// our buffer
static char BUFFER[BUFFER_SIZE];
int size = (sizeof(message)/sizeof(message[0]))-1;

//out in and out positions
static int in;
static int out;

//semaphores declaration
sem_t manage_empty;
sem_t manage_full;
sem_t manage_cs;

//declaration of threads
pthread_t producers[PRODUCER_SIZE];
pthread_t consumer;

void* consumer_function(void* arg){

char c_buff;

sem_wait(&manage_full);
sem_wait(&manage_cs);
///////////CS//////////
while(out < in){

c_buff = BUFFER[out];
printf("%c\n",c_buff);
out++;
}
in = 0;
///////////END_CS//////
sem_post(&manage_cs);
sem_post(&manage_empty);

return NULL;
}//end consumer_function

void* producer_function(void* arg){

char p_buff;

sem_wait(&manage_empty);
sem_wait(&manage_cs);
/////////CS////////////
while(in < BUFFER_SIZE){
p_buff = message[msgindex];
BUFFER[in] = p_buff;
in++;
msgindex++;
}
out = 0;
////////END_CS/////////
sem_post(&manage_cs);
sem_post(&manage_full);

return NULL;
}//end producer_function

int main(){

//initialize semaphore
sem_init(&manage_empty, 0, 1);
sem_init(&manage_full, 0, 0);
sem_init(&manage_cs, 0, 1);

//initialize variable counters
in = 0;
out = 0;

//creation of threads
int x;
    for(x=0;x<PRODUCER_SIZE;x++){
        pthread_create(&producers[x],NULL,producer_function,NULL);
    }//end for loop

    pthread_create(&consumer,NULL,consumer_function,NULL);

    for(x=0;x<PRODUCER_SIZE;x++){
        pthread_join(producers[x],NULL);
    }//end for loop

    pthread_join(consumer,NULL);

    sem_destroy(&manage_empty); 
    sem_destroy(&manage_full); 
    sem_destroy(&manage_cs); 

return 1;
}//end main function

Ответы [ 2 ]

0 голосов
/ 13 июля 2020

Ваши производители производят всю строку за один l oop, а потребитель потребляет всю строку за один l oop, так что максимум у вас есть один потребитель и один производитель l oop, выполняющий всю работу .

Вместо этого рассмотрите следующий подход:

static volatile int buffer_data;      /* A single-character buffer */
static sem_t        buffer_produced;
static sem_t        buffer_consumed;

void *consumer(void *unused) {
    while (1) {

        /* Wait for data in the buffer. */
        sem_wait(&buffer_produced);

        /* sleep(1); */

        /* End of data? */
        if (buffer_data == EOF) {
            /* If there are other consumers, wake up next one */
            sem_post(&buffer_produced);
            break;
        }

        /* Consume it. */
        fputc(stdout, buffer_data);
        fflush(stdout);

        sem_post(&buffer_consumed);
    }
    return NULL;
}

Потребитель ожидает семафор buffer_produced (который должен быть инициализирован нулем). Затем он проверяет произведенные данные, buffer_data, который представляет собой один символ, или EOF, чтобы указать, что данных больше нет.

Если есть более одного потребителя, нам нужно опубликовать buffer_produced семафор, чтобы каждый потребитель тоже видел EOF. Затем поток завершается. (Все потребители будут выходить более или менее группами, при этом произведено EOF.)

Если данные не EOF, потребитель распечатывает их на стандартный вывод. Поскольку стандартный вывод по умолчанию буферизуется, мы fflush(stdout), чтобы напечатанный символ доставлялся на экран (или канал или файл, если стандартный вывод перенаправлен на что-то).

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

Закомментированный sleep(1); задержит печать каждого символа на одну секунду, поэтому вы увидите символы появляются с интервалом в одну секунду.

Производители, которые создают строковое сообщение, могут быть примерно такими:

static const char message_str[] = "Hello, world!\n";
static size_t     message_pos = 0;

void *producer(void *) {
    int  c;

    do {

        sem_wait(&buffer_consumed);

        c = message_str[message_pos];
        if (c != '\0') {
            buffer_data = c;
            message_pos++;
        } else {
            buffer_data = EOF;
        }
        sem_post(&buffer_produced);

    } while (c != EOF);

    return NULL;
}

Производитель ожидает buffer_consumed семафор, который должен быть инициализирован 1 По сути, когда buffer_consumed == 1, это означает, что буфер пуст.

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

Производитель устанавливает buffer_data на символ или EOF, и размещает семафор buffer_produced, чтобы потребитель мог его использовать.

Если это было EOF, производитель готов и может выйти.

Обратите внимание, что когда message_pos указывает на нулевой символ конца строки, '\0', мы не увеличиваем его. Это сделано для того, чтобы, если у вас несколько производителей, они тоже увидят нулевой символ конца строки (создавая «другой» EOF) и завершат работу.

Если производителей на N больше, чем потребителей, то после возвращения последнего потребителя основной поток должен отправить N семафор buffer_consumed, чтобы все производители проснулись и вышли. (Итак, если у вас есть 1 потребитель и 4 производителя, вам нужно sem_post(&buffer_consumed) 3 раза.)

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

0 голосов
/ 13 июля 2020

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

И это возможно, потому что вы инициализируете все семафоры, чтобы значение 1, тогда как кажется, что вы хотели бы инициализировать manage_full значением 0, чтобы потребитель ждал, пока что-то не будет произведено, прежде чем пытаться что-либо потребить.

Однако даже тогда ваши производители работают в одноразовый режим тоже. Каждый из них будет производить не более одного символа перед своим завершением.

Кроме того, у вас есть как минимум один слишком много объектов синхронизации. Обратите внимание, как ни один поток не получает мьютекс lock без предварительного уменьшения семафора manage_cs, как этот семафор инициализируется значением 1 и как ни один поток не отправляет ему сообщение без предварительного уменьшения его значения. Это отличный способ использовать manage_cs в качестве мьютекса, и в этом случае вам не нужен другой мьютекс, который всегда и блокируется только тогда, когда manage_cs заблокирован.

Более того, ваш производитель дважды увеличивает переменную in, в результате чего первый выходной символ записывается в буфер с индексом 1, а не 0. Кроме того, он сбрасывает in на ноль после создания двух символов, но это не имеет смысла, потому что он используется в качестве индекса следующего символа, создаваемого из данных. Таким образом, если потребитель печатает символ, то это определенно будет нулевой символ, которым инициализируется BUFFER[0], а не что-либо из строки данных.

Могут возникнуть другие проблемы, например хорошо.

...