2014-03-19 44 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