C - потребитель / производитель появляется в тупике, когда производитель больше размера буфера - PullRequest
1 голос
/ 27 марта 2012

У меня есть задание для работы над проблемой производителя и потребителя с использованием потока и семафора. Задача состоит в том, чтобы пользователь мог определить # производителя, # потребителя и размер буфера. Программа всегда блокируется, если производитель достигает размера буфера. Но требование гласит, что если производитель достигает буферов, поток потребителя должен запускаться и принимать данные из буфера. Я не знаю, как решить эту проблему, и мой учитель отказывается помочь. Я полностью новичок в языке Си, пожалуйста, дайте мне несколько советов. Большое спасибо

Моя программа может запускаться, когда Producer = Consumer или Producer Buffer Size, кажется, что это тупик, и я думаю, что понимаю причину, но не знаю, как исправить код, чтобы Потребительский поток запускается первым, а затем возвращается к потоку производителя.

Вот результат выполнения, когда производитель = 3, потребитель = 1 и размер буфера = 2

./Task2 3 1 2
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Producer 2 has started

Требование говорит, что результат должен выглядеть следующим образом:

Started
Producer 0 has started
Producer 0: Put item 0.
Producer 1 has started
Producer 1: Put item 1.
Producer 2 has started
Consumer 0 has started
Consumer 0: Taked item 0.
Producer 2: Put item 2.
Terminated!

Вот мой исходный код, я отбросил код проверки ошибки ввода

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

pthread_t *pid, *cid;

void *producer(void *param); 
void *consumer(void *param); 
void init();
int Remove();

struct prot_buffer{
int Producer;
int Consumer;
int *buffer;        
int buffersize;
int front;      
int rear;           
int item;           
sem_t mutex;        
sem_t slots;
sem_t items;
}b;


main(int argc, char *argv[]){
int c1; 

b.Producer = atoi(argv[1]);
b.Consumer = atoi(argv[2]);
b.buffersize = atoi(argv[3]);

init(); 

pid = (pthread_t *)malloc(b.Producer *sizeof(pthread_t));
cid = (pthread_t *)malloc(b.Consumer *sizeof(pthread_t));

for (c1=0; c1< b.Producer; c1++){
    printf("Producer %d has started\n", c1);
    pthread_create(&(pid[c1]),NULL, producer, NULL);
    pthread_join(pid[c1], NULL);
    printf("Producer %d:Create item %d.\n", c1,c1);
}


/* Create the consumer threads */
for (c1=0; c1<b.Consumer; c1++){
    printf("Consumer %d has started\n", c1);
    pthread_create(&(cid[c1]),NULL, consumer, NULL);
        if (b.front==b.rear){
        printf("Terminated!\n");
        exit(0);
        }  
    pthread_join(cid[c1], NULL);
    printf("Consumer %d:Taked item %d.\n", c1, c1);
}  

free(b.buffer);
free(pid);
free(cid);

sem_destroy(&b.items);
sem_destroy(&b.slots);
sem_destroy(&b.mutex);

printf("Threads terminated!\n");
exit(0);
}

void *producer(void *param){
sem_wait(&b.slots);                             sem_wait(&b.mutex);

if(b.rear<=b.buffersize){
    b.buffer[b.rear] = b.item; 
    b.rear++;
    sem_post(&b.mutex);                             sem_post(&b.items);                     
}else{
sem_post(&b.mutex);                             sem_post(&b.items);                             }
}

void *consumer(void *param){
Remove();
}

void init(){
b.buffer = (int *) malloc(b.buffersize *sizeof(int));
b.buffersize = b.buffersize;
b.front = b.rear =0;
sem_init(&b.items, 0, 0);
sem_init(&b.slots,0,b.buffersize);
sem_init(&b.mutex, 0, 1);
}

int Remove(){
sem_wait(&b.items);                     
sem_wait(&b.mutex);                     

b.item = b.buffer[b.front];             
b.front++;

sem_post(&b.mutex);                     
sem_post(&b.slots);                     
return b.item;
}

Мой новый код

main(int argc, char *argv[]){
...

pthread_create(&pid,NULL, producer, NULL);
pthread_create(&cid,NULL, consumer, NULL);
....
}

void *producer(void *param){
int c2;

for (c2=0; c2 < b.Producer; c2++) {
printf("Producer %d has started\n", c2);
b.item = c2;

sem_wait(&b.slots);
sem_wait(&b.mutex); 

b.buffer[b.rear] = b.item;
b.rear = (b.rear+1)%b.buffersize;
printf("Producer %d:Put item %d.\n", c2,c2);

sem_post(&b.mutex);
sem_post(&b.items);

}
return NULL;
}

void *consumer(void *param){
int c2;

for (c2=0; c2 < b.Consumer; c2++) {
printf("Consumer %d has started\n", c2,c2);
b.item = c2;

sem_wait(&b.items);
sem_wait(&b.mutex); 

b.buffer[b.front] = b.item;
b.front = (b.front+1)%b.buffersize;

printf("Consumer %d:take item %d.\n", c2, c2);

sem_post(&b.mutex);
sem_post(&b.slots);

}
return NULL;
}

Чтобы избежать неприятностей в школе, я удалил код и описание.

Результат программы правильный, спасибо за помощь. В этом случае я использую b.item в качестве переменной для отображения элемента, оставленного внутри буфера, но это неправильно. Использование других переменных, таких как front или back, также не работает.

Результат программы -

Производитель = 2, Потребитель = 2, Буфер = 2

./F 2 2 2
started
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Consumer 0 has started
Consumer 0:Take item 0.
Consumer 1 has started
Consumer 1:Take item 1.
1 item(s) left in the buffer!  //This is wrong!
Terminated!

Производитель = 3, Потребитель = 1, Буфер = 2

./F 3 1 2
started
Producer 0 has started
Producer 0:Deposited item 0.
Producer 1 has started
Producer 1:Deposited item 1.
Producer 2 has started
Consumer 0 has started
Consumer 0:Removed item 0.
Producer 2:Deposited item 2.
0 item(s) left in the buffer!  //Still wrong!
Terminated!

Производитель = 2, Потребитель = 5, Буфер = 3

./F 2 5 3
started
Producer 0 has started
Producer 0:Put item 0.
Producer 1 has started
Producer 1:Put item 1.
Consumer 0 has started
Consumer 0:Take item 0.
Consumer 1 has started
Consumer 1:Take item 1.
Consumer 2 has started
2 item(s) left in the buffer!  //Wrong again!
Terminated!

Ответы [ 2 ]

2 голосов
/ 27 марта 2012

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

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

0 голосов
/ 27 марта 2012

Ваша инициализация и использование семафоров кажутся правильными - они являются естественным выбором для очередей производитель-потребитель - это не condvars (condvars не имеют счетчика и поэтому требуют циклов while для корректной работы, даже если ваша ОС не поддерживаетфункция ложного пробуждения).

Однако использование вами буфера выглядит немного «выключенным».Я предполагаю, что буфер должен быть круглым?Если это так, вы должны устанавливать индекс обратно в 0 всякий раз, когда передний или задний индекс достигает конца массива.Вы должны делать это внутри блокировки мьютекса в коде производителя и потребителя.Проверка индексов должна быть исключительно для сброса их в начало буфера и не должна каким-либо образом изменять работу сигнала / ожидания семафора.

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

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

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

...