2014-03-19 45 views
1

我目前正在學習houw使用Pthreads和信號量,我一直在制定者/消費者問題的實施,但程序只是掛起。我知道它到達消費者代碼,運行比較一次,然後在比較p1_string和p2_string的默認初始值後掛起,我真的不明白我究竟做了什麼錯誤。生產者/消費者使用Pthreads和信號燈

基本上每個生產者線程都應該採用一個排序行文件並將一行讀入內存。然後主線程應該比較這兩個字符串,並按照排序順序輸出。

#include <iostream> 
#include <fstream> 
#include <string> 
#include <cstdlib> 
#include <cstring> 
#include <cerrno> 
#include <unistd.h> 
#include <pthread.h> 
#include <semaphore.h> 
using namespace std; 

sem_t p1_empty,p2_empty,p1_full,p2_full; 
string p1_string="atest", p2_string="btest"; 

typedef struct { 
    char* filename; 
    string buffer; 
    sem_t empty; 
    sem_t full; 
} pthread_param; 

void* producer(void* arg) { 
    pthread_param* ptp = (pthread_param*)arg; 
    ifstream input(ptp->filename); 
    if (not input) { 
     cerr << "Can't open file \"" << ptp->filename << "\".\n"; 
     exit(EXIT_FAILURE); 
    } 
    while(getline(input,ptp->buffer)) { 
     sem_post(&ptp->full); 
     sem_wait(&ptp->empty); 
    } 
    ptp->buffer = "\x7f"; 
} 

int main(int argc, char* argv[]) { 
    if (argc != 3) { 
     cerr << "Syntax: " << argv[0] << " filename filename\n"; 
     exit(EXIT_FAILURE); 
    } 
    //init threads, variables and semaphores 
    sem_init(&p1_empty,0,0); 
    sem_init(&p2_empty,0,0); 
    sem_init(&p1_full,0,0); 
    sem_init(&p2_full,0,0); 
    pthread_t p1_thread, p2_thread; 
    pthread_param pt1_param; 
    pthread_param pt2_param; 
    pt1_param.filename = argv[1]; 
    pt2_param.filename = argv[2]; 
    pt1_param.buffer = p1_string; 
    pt2_param.buffer = p2_string; 
    pt1_param.empty = p1_empty; 
    pt2_param.empty = p2_empty; 
    pt1_param.full = p1_full; 
    pt2_param.full = p2_full; 
    pthread_create(&p1_thread,nullptr,producer,&pt1_param); 
    pthread_create(&p2_thread,nullptr,producer,&pt2_param); 

    /* testing to make sure producer reads correctly 
    pthread_param* ptp = &pt1_param; 
    ifstream input(ptp->filename); 
    if (not input) { 
     cerr << "Can't open file \"" << ptp->filename << "\".\n"; 
     exit(EXIT_FAILURE); 
    } 
    while(getline(input,ptp->buffer)) { 
     cout<<ptp->buffer<<endl; 
    } 
    ptp->buffer = "\x7f"; 
*/ 

    //consumer 
    while(pt1_param.buffer != "\x7f" && pt2_param.buffer != "\x7f"){ 
     if(pt1_param.buffer <= pt2_param.buffer) { 
     cout<<pt1_param.buffer<<endl; 
     sem_post(&p1_empty); 
     sem_wait(&p1_full); 
     } 
     else { 
     cout << pt2_param.buffer <<endl; 
     sem_post(&p2_empty); 
     sem_wait(&p2_full); 
     } 
    } 

    //delete threads/semaphores 
    pthread_join(p1_thread,nullptr); 
    pthread_join(p2_thread,nullptr); 
    sem_destroy(&p1_empty); 
    sem_destroy(&p2_empty); 
    sem_destroy(&p2_full); 
    sem_destroy(&p2_full); 
    return 0; 
} 
+0

這是什麼? '如果(不輸入)'?你是不是指'如果(!輸入)'?這不是Python ;-) – AndyG

+0

此外,它似乎並沒有真正創建任何消費者線程。你想在'main'裏面完成什麼? – AndyG

+0

基本上每個生產者都需要一個已排序行的文件,並需要讀取一行並將其另存爲一個字符串。然後,當主線程需要比較這兩個生產者時,選擇較小的字符串,將其寫入輸出,然後提示該線程獲取新字符串。 – Ashtoruin

回答

1

1)的=在由值的字符串拷貝操作。以下代碼按值複製到'buffer'變量中。但是,稍後您將在無限循環中使用p1_string,以期更新它。爲pthread_param.buffer分配新值不會更改px_string的值。因此,在這種情況下,字符串將總是等於其初始值,並串1將總是小於串2.

pt1_param.buffer = p1_string; //assignment by value 
pt2_param.buffer = p2_string; //assignment by value 


2)考慮在以下代碼競爭狀態。生產者中的getline()函數和if/cout代碼都可以同時訪問緩衝區變量。在生產者以下重新排列的(僞)代碼的順序可能看起來非常相似,但是,它確實改變行爲頗有幾分:

while (1) 
{ 
    sem_wait() 
    if (!getline(buffer)) 
     break; 
    sem_post() 
} 

現在,生產者必須立即阻止,等到他們從消費者那裏得到一個信號,告訴它完成訪問緩衝區變量。它會在帖子前面調用wait(),這會產生非常顯着的效果,我會嘗試描述。以前,生產者和消費者都在sem_wait之前調用sem_post,並且相應的信號計數都增加了。因此,當生產者試圖等待()它只是最終減少已有的計數,並繼續。由於消費者已經增加了信號量,生產者也會發生同樣的情況。因此,在生產者和消費者中循環的每一次迭代都變成了使用緩衝變量的不可預測的競爭情況。

+0

這非常有意義,我更改了代碼以反映您的建議,但可悲的是它仍然無效:/ – Ashtoruin

+0

您的代碼中仍有競爭條件。考慮一下情況:生產者中的getline()正在修改緩衝區,而主線程將其打印出來或與其進行比較...... – Deque