2012-03-27 76 views
1

我有使用線程和信號量來處理生產者和消費者問題的任務。其任務是允許用戶定義生產者數量,消費者數量和緩衝區大小。如果生產者到達緩衝區大小,程序總是鎖定。但是要求說如果生產者達到緩衝區,消費者線程應該開始並從緩衝區中取出東西。我不知道如何解決這個問題,我的老師拒絕幫助。我完全是C語言的初學者,請給我一些建議。非常感謝C - 當生產者大於緩衝區大小時,消費者/生產者會出現死鎖

當Producer = Consumer或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作爲變量來顯示留在緩衝區內的項目,但它的錯誤。使用其他變量,如前面或後面也不工作。

方案result-

生產者= 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 ,Consumer = 5,Buffer = 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! 
+0

投射'malloc'的返回值是不必要的。 – 2012-03-27 09:56:50

+1

你是否被限制使用信號量?你不能使用條件變量嗎?他們似乎是這種情況下更自然的選擇。 – Tudor 2012-03-27 10:02:14

+0

@Tudor是的,任務的要求表示我們必須使用信號量。 – 2012-03-27 10:16:27

回答

2

您的緩衝區大小爲2.前2個生產者填滿此緩衝區。因此,第三個等待消費者購買一件商品,以便它可以添加到緩衝區中。 但生產者環路內的pthread_join決不允許創建消費者!pthread_join正在暫停main進程,直到第3個生產者終止。因此,第三個生產者無限期地等待緩衝區被永遠不會到達的消費者釋放的死鎖。

我建議你通過Exercise 3這篇文章處理完全相同的問題和相同的數據結構作爲你的。他們已經清楚地闡述瞭如何防止緩衝區溢出,在哪裏打印生產者數據。可能是其對於畢業生的標準信號量教程練習。

+0

加入()災難的+1。令人遺憾的是,許多開發人員(和線程示例)似乎都急於與線程連接(),無論其他方法是否可以避免硬鎖連接()和類似的死鎖生成器的危險。 – 2012-03-27 12:00:46

+0

@馬丁詹姆斯非常感謝,這個練習真的很有幫助。在我還注意到由_join()引起的問題之前,我不知道如何糾正我的程序。我在練習中重寫了我的程序基礎,結果看起來不錯,但仍然存在一些問題。看起來生產者線程沒有足夠長的睡眠時間,並開始循環,從我的兩個新的2個結果中我發現生產者將保持創建,即使沒有更多的緩衝區空間。你能告訴我我應該在哪裏糾正我的代碼?如果有3個生產者,生產者應該創建3次而不是循環一次又一次。非常感謝 – 2012-03-28 08:22:57

+0

@ShawnLien我現在很忙,我會回來編輯我的答案,以便在代碼 – 2012-03-28 09:44:06

0

您的信號量初始化和使用看起來是正確的 - 它們是生產者 - 消費者隊列的自然選擇 - 它不是的condvars,(condvars沒有計數,因此需要while while循環才能正確運行,即使您的操作系統不支持僞造喚醒功能)。

然而,您對緩衝區的使用似乎有點「關閉」。我認爲緩衝區應該是循環的?如果是這樣,只要前面或後面的索引到達數組的末尾,您應該將索引設置回0。您應該在生產者代碼和使用者代碼中的互斥鎖內部執行此操作。對索引的檢查應該只是將它們重置爲緩衝區的開始,並且不應該以任何方式改變信號量信號/等待的操作。

那麼,你似乎一旦創建它們就加入這些線程。通常,爲了測試/演示生產者/消費者操作,線程不會不斷創建,立即加入,以便構建線程必須立即等待,然後結束。運行一些生產者和消費者線程循環,不斷產生/消耗消息(可能只有很短的睡眠循環 - 一些開發人員說沒有有效使用並且是反模式的循環)更爲常見。

不斷創建/終止/銷燬線程非常浪費,並且會導致在您的測試代碼中執行幾乎所有開銷和非常少的生產者/使用者的應用程序。

最後一件事 - 不要加入到線索中,而不要一兩秒鐘之後再思考它。它可能不是最佳的,也可能不是必要的,它可能是一個障礙,它可能是一場災難。如果連接僅僅是爲了保持你的主線程(以及處理過程),那麼最好是找到另一種方式 - 等待鍵盤輸入或使用長期sleep()循環。