C - 当生产者大于缓冲区大小时,消费者/生产者出现死锁

时间:2012-03-27 09:53:24

标签: c multithreading pthreads semaphore producer-consumer

我通过使用线程和信号量来分配生产者和消费者问题。任务是允许用户定义生产者的#,消费者的#和缓冲区大小。如果生产者到达buffersize,程序总是锁定。但是要求说如果生产者达到缓冲区,则消费者线程应该从缓冲区开始并从中获取内容。我不知道如何解决这个问题,老师拒绝帮忙。我是C语言的初学者,请给我一些建议。非常感谢你

我的程序可以在Producer = Consumer或Producer<消费者,生产者除外>缓冲区大小,它似乎出现死锁,我想我理解其中的原因,但我不知道如何修复代码让Consumer线程首先运行而不是返回到Producer线程。

当producer = 3 consumer = 1且buffersize = 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作为变量来显示缓冲区内的项目,但错误。使用其他变量如前或后也不起作用。

计划结果 -

生产者= 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 个答案:

答案 0 :(得分:2)

您的缓冲区大小为2.前两个生成器填满此缓冲区。因此,第三个是等待消费者拿一个项目,以便它可以添加到缓冲区。 但是生产者循环中的pthread_join永远不允许创建消费者! pthread_join暂停main进程,直到第3个生成器终止。因此,第三个Producer中的死锁无限期地等待缓冲区被永不到来的消费者释放。

我建议你仔细阅读本文的Exercise 3,其中涉及与您完全相同的问题和相同的数据结构。他们清楚地阐明了如何防止缓冲区溢出,在哪里打印生产者数据。可能是它为毕业生提供的标准信号量教程练习。

答案 1 :(得分:0)

您的信号量的初始化和使用似乎是正确的 - 它们是生产者 - 消费者队列的自然选择 - 它是不是的条件,(condvars没有计数,因此即使您的操作系统不支持,也需要while循环才能正确操作)虚假的唤醒功能)。

然而,你对缓冲区的使用似乎有点“关闭”。我认为缓冲区应该是循环的?如果是这样,只要前面或后面的索引到达数组的末尾,就应该将索引设置回0。您应该在生产者和消费者代码中的互斥锁内执行此操作。检查索引应该只是将它们重置为缓冲区的开头,不应该以任何方式改变信号量信号/等待的操作。

那,你似乎在创建线程后立即加入线程。通常,为了测试/演示生产者/消费者操作,不会不断创建线程,立即加入线程(以便构造线程必须立即等待),然后离开以终止。运行一些生产者和消费者线程更为常见,这些线程循环,不断产生/消费消息,(可能是一个非常短的睡眠循环 - 一些开发人员认为没有有效用途并且是反模式的那种循环)。 / p>

持续创建/终止/销毁线程非常浪费,并且会在测试代码中导致几乎所有开销和非常少的生产者/消费者执行的应用程序。

最后一件事 - 不要在没有考虑它的情况下加入线程一两秒钟。它可能不是最佳的,它可能没有必要,它可能是一个障碍,它可能是一场灾难。如果连接只是为了让你的主线程(以及进程)保持提前终止,那么最好找另一种方法 - 等待键盘输入或使用长期sleep()循环。