使用Pthreads和Semaphores的生产者/消费者

时间:2014-03-19 04:00:21

标签: c++ pthreads semaphore

我目前正在学习如何使用Pthreads和Semaphores,我一直致力于生产者/消费者问题的实施,但该计划只是暂停。我知道它进入消费者代码,运行比较一次,然后在默认初始化值比较p1_string和p2_string之后挂起,我真的不明白我做错了什么。

基本上每个生产者线程都应该采用排序行的文件并将一行读入内存。然后主线程应该比较这两个字符串,并按排序顺序写出来输出。

#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;
}

1 个答案:

答案 0 :(得分:1)

1)字符串上的=运算符按值复制。以下代码按值复制到“缓冲区”变量中。但是,稍后您在无限循环中使用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,并且相应的信号量计数都会递增。因此,当生产者试图等待()时,它最终会减少已经存在的计数并继续。由于消费者已经增加了其信号量,生产者也会发生同样的事情。因此,生产者和消费者中循环的每次迭代都会变成一种不可预测的种族情况,以便使用缓冲变量。