多生产者多个消费者线程问题

时间:2013-04-19 03:12:18

标签: c synchronization pthreads producer-consumer

我有一个程序,我正在尝试实现多生产者,多用户设置。我有一个代码,当我有一个消费者和多个生产者时似乎运作良好,但引入多个消费者线程似乎引发了一些奇怪的问题。

这就是我现在所拥有的:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

#define MAX 10

typedef struct travs {
    int id;
    int numBags;
    int arrTime;
    struct travs *next;
} travs;


travs *queue;
//travs *servicing[MAX];

int produced; // The total # of produced in the queue

pthread_mutex_t queue_lock;
//pthread_mutex_t staff_lock;
pthread_cond_t ct, cs;

int CheckIn(){
    sleep(1);
    if(produced != 0) return 1;
    else return 0;
}



void *producerThread(void *args){
    travs *traveler = (travs *)args;
    // Acquire the mutex
    pthread_mutex_lock(&queue_lock);
    produced++;
//  pthread_cond_signal(&cs);
    pthread_cond_wait(&ct, &queue_lock);
    printf("Producer %d is now checked in at time %d.\n", queue->id, (1+queue-    >arrTime));
    queue = queue->next;
    pthread_mutex_unlock(&queue_lock);

    return; 
}       

int Producer(int id, int numBags, int arrTime){

    int ret;
    pthread_t ttid;
    travs *traveler = malloc(sizeof(travs));
    traveler->id = id;
    traveler->numBags = numBags;
    traveler->arrTime = arrTime;
    sleep(arrTime); 
    pthread_mutex_lock(&queue_lock);
    if(queue != NULL) {
        travs *check_in = malloc(sizeof(travs));
        check_in = queue;
        while(check_in->next != NULL){
            check_in = check_in->next;
        }
        check_in->next = traveler;
    }
    else { queue = traveler; }
    pthread_mutex_unlock(&queue_lock);
    // Create a new traveler thread
    ret = pthread_create(&ttid, NULL, producerThread, (void *)traveler);

    // Check if thread creation was successful
    if(ret == 0) {
        printf("Producer %d has entered the check-in line at time %d; s/he is at     position %d and has %d bags.\n", id, arrTime, produced, numBags);
        pthread_cond_signal(&cs);
        return 0;
    }
    else return -1;

}


void *consumerThread(void *arg){

    int i = 0; // travelers serviced
    char *name = (char *)arg;
    while(1) { // run iteratively

        // If 20 producers have been served, the consumer's work is done.
        if(i == 20) {
            printf("Consumer %s's service has completed!\n", name);
                pthread_exit(NULL);
            }
        // Sleep for 10s if 5 travelers have been checked in
        if (((i+1) % 5) == 0) {
                // Wake up sleeping travelers
                printf("Consumer %s is taking a break.\n", name);
                sleep(2);
                printf("Consumer %s's break is over.\n", name);
        }

        if(CheckIn()) {
            pthread_mutex_lock(&queue_lock);
            int j = 1;
                    pthread_cond_wait(&cs, &queue_lock);
                    printf("Producer %d presents ticket to consumer     %s.\n", queue->id, name);
                    printf("Consumer %s gives boarding pass to producer     %d.\n", name, queue->id);
                    while(j <= queue->numBags){
                        printf("Consumer %s checks in bag %d for     producer %d; baggage tag is _X_.\n", name, j, queue->id);
                        j++;
                }
            // Signal producer being serviced that their check in is complete.
            i++;
            pthread_mutex_unlock(&queue_lock);
            produced--;
            pthread_cond_signal(&ct);
        }
    sleep(3);
    }
}

int Consumer(char *Name) {

    sleep(5);
    int ret;
    pthread_t stid;
    // Create a staff thread

    ret = pthread_create(&stid, NULL, consumerThread, (void *)Name);
    // Acquire the lock
    if(ret == 0) { 
        printf("Producer %s's service has begun!\n", Name);
        return 0;
    }
    else    return -1;
}

int main() {
    int ret = 0;
    char *staff_name = malloc(sizeof(char));
    int staff_check = 0;
    int trav_check = 0;
    int id;
    int bagnum;
    int travtime;
    FILE *consumer_fp;
    FILE *producer_fp;
    queue = malloc(sizeof(travs));
    queue = NULL;
    /*while(ret < 10){
        servicing[ret] = malloc(sizeof(travs));
        servicing[ret] = NULL;
    }*/

    // Initilize mutexes
    pthread_mutex_init(&queue_lock, NULL);
    //pthread_mutex_init(&staff_lock, NULL);

    // Initialize condition variables
    pthread_cond_init(&ct, NULL);
    pthread_cond_init(&cs, NULL);

    // Open the file so we can start reading from it

    consumer_fp = fopen("staff.txt", "r");
    producer_fp = fopen("travelers.txt", "r");

    staff_check = fscanf(consumer_fp, "%s", staff_name);
    trav_check = fscanf(producer_fp, "%d %d %d", &id, &bagnum, &travtime);
    while(1){   

        K:
        while(staff_check == 1) {
            Consumer(staff_name);
            staff_check = fscanf(consumer_fp, "%s", staff_name);
            goto L;
        }
        L:
        while(trav_check == 3) { 
            Producer(id, bagnum, travtime);
            trav_check = fscanf(producer_fp, "%d %d %d", &id, &bagnum,     &travtime);
            goto K;
        }

    pthread_exit(NULL);
    }

}

在这个设置中,每个生成器线程在返回之前只存在很短的时间,并且除了向全局队列添加新元素和几个适当的定时输出行之外,它本身没有真正的计算。

然而,当我引入多个生成器时,只有最后一个生成器线程做任何事情。

据我所知,我需要以下内容:

i)等待登记的生产者的单独队列和当前正在登记的生产者(注释为travs *服务上面的[MAX])

ii)为消费者提供单独的互斥锁。

但是,我不确定如何实现这一点。这是我想到的想法:

  1. CheckIn()生成器线程并将* queue复制到* servicing [i](在使用者线程中)。

  2. 设置queue = queue-&gt; next(在生产者线程中)。

  3. 但是,我怎么能保证当我复制*队列时,它已经不会提前一步?我可以通过一个与当前线程所持有的锁不同的锁来发出等待线程的信号吗?而且,更重要的是,我如何让不同的消费者线程处理不同的旅行者线程?

    非常感谢任何援助!

2 个答案:

答案 0 :(得分:4)

正如我所提到的,这是内存泄漏:

travs *traveler = malloc(sizeof(travs));
traveler = (travs *)args;

我不打算详细介绍“内存泄漏有多糟糕?”。如果您想要这个答案,请向Google询问该问题。你可能意味着:travs *traveler = args;


if(queue != NULL) {
    travs *check_in = malloc(sizeof(travs));
    check_in = queue;
    while(check_in->next != NULL){
        check_in = check_in->next;
    }
    check_in->next = traveler;
}
else { queue = traveler; }
抛开内存泄漏,为什么队列互斥锁先前在其他函数中被保护,而在此代码中没有互斥锁保护 ?好像你错过了互斥体的观点。你的代码在这里比赛。

或许pthread_rwlock_t更适合这种代码。

答案 1 :(得分:3)

使用一个队列。

编写两个函数,一个用于将现有项添加到队列中,另一个用于从队列中删除项。不要在这些功能中使用任何锁定。在单线程应用程序中测试它们。

然后为这两个add-and-remove-functions写两个包装器。那些包装器应该使用一个额外的互斥量作为参数。在调用add-或remove-function之前将此互斥锁锁定在包装器中,然后解锁互斥锁。

编写生成器线程函数,创建一个新项并调用add-item-wrapper。编写消费者线程函数,调用remove-item-wrapper并将删除的项目存档。

设置main()函数声明并初始化互斥锁,然后使用pthread_create()创建生产者和使用者的各种实例。将互斥锁作为参数传递给线程函数。