如何获得两个pthread线程来回应彼此'等待和信号条件?

时间:2014-06-19 08:35:37

标签: c multithreading pthreads

我在使用基本的双线程安排时遇到了一些麻烦。

我正在一个"生产者"中从stdin读取一大块字节到内存中。线程,并在第二个"消费者中处理这些字节"线程,一旦这些字节可用。一旦消耗了字节,消费者线程就会回到休眠状态,生产者线程再次运行。

我正在使用pthread_cond_wait()pthread_cond_signal()让两个线程相互通信生成或使用数据。

这是两个线程的代码:

void * produce_bytes(void *t_data)
{ 
    pthread_data_t *d = (pthread_data_t *)t_data;

    do {
        pthread_mutex_lock(&d->input_lock);
        d->n_bytes = fread(d->in_buf, sizeof(unsigned char), BUF_LENGTH_VALUE, stdin);
        if (d->n_bytes > 0) { 
            fprintf(stdout, "PRODUCER ...signaling consumer...\n");
            pthread_cond_signal(&d->input_cond);
            fprintf(stdout, "PRODUCER ...consumer signaled...\n");
        }
        pthread_mutex_unlock(&d->input_lock);
    } while (d->n_bytes > 0);

    return NULL;
}

void * consume_bytes(void *t_data) 
{
    pthread_data_t *d = (pthread_data_t *)t_data;

    pthread_mutex_lock(&d->input_lock);
    while (d->n_bytes == 0)
        pthread_cond_wait(&d->input_cond, &d->input_lock);
    fprintf(stdout, "CONSUMER ...consuming chunk...\n");
    d->n_bytes = 0;
    fprintf(stdout, "CONSUMER ...chunk consumed...\n");
    pthread_mutex_unlock(&d->input_lock);
}

pthread_data_t是我用来跟踪状态的结构:

typedef struct {
    pthread_mutex_t input_lock;
    pthread_cond_t input_cond;
    unsigned char in_buf[BUF_LENGTH_VALUE];
    size_t n_bytes;
} pthread_data_t;

我在main()函数中配置变量;这是相关的摘录:

pthread_t producer_thread = NULL;
pthread_t consumer_thread = NULL;
pthread_data_t *thread_data = NULL;

thread_data = malloc(sizeof(pthread_data_t));
thread_data->n_bytes = 0;
pthread_mutex_init(&(thread_data->input_lock), NULL);
pthread_cond_init(&(thread_data->input_cond), NULL);

pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);

pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);

当我运行它时,produce_bytes()在第一次迭代时成功发出consume_bytes()信号,但在第二次和后续迭代中,信号被发送到consume_bytes()并且它永远不会被听到,所以消费者功能永远不会再次运行:

PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
CONSUMER ...consuming chunk...
CONSUMER ...chunk consumed...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
PRODUCER ...signaling consumer...
PRODUCER ...consumer signaled...
...

我正在使用教程here作为我尝试做的事情的基础。我做错了什么?

2 个答案:

答案 0 :(得分:1)

该代码存在一些问题:

  1. produce_bytes会在阻止调用fread期间锁定互斥锁。响应式应用程序的一般经验法则是尽可能短时间锁定互斥锁。您可能希望首先将输入读入临时缓冲区,然后锁定互斥锁并将数据复制到线程之间共享的缓冲区。同样适用于consume_bytes,它在调用可以阻止的fprintf时保留互斥锁。
  2. produce_bytes中的
  3. while(d->n_bytes > 0)不会保留互斥锁,这是一种竞争条件,因为consume_bytes会为d->n_bytes分配一个新值。假设您希望在fread返回0(EOF)时退出该循环,则需要将fread的返回值复制到线程之间未共享的局部变量中,并将其用作{while(read_bytes > 0)中的条件。 1}}
  4. consume_bytes周围没有任何循环,因此它会在第一个条件变量通知后返回。您可能希望将其包装到while循环中,并仅在读取EOF(0字节)时退出。

答案 1 :(得分:0)

这是一个工作示例,它解决了Maxim的第2点和第3点,但不是1,因为这是响应性所必需的,但不是严格要求正确性。

请注意,我没有为生产者实现向消费者发出EOF信号的方法,因此消费者永远不会退出。

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

#define  BUF_LENGTH_VALUE 100
typedef struct {
    pthread_mutex_t input_lock;
    pthread_cond_t input_cond;
    unsigned char in_buf[BUF_LENGTH_VALUE];
    size_t n_bytes;
} pthread_data_t;

void * produce_bytes(void *t_data)
{ 
    pthread_data_t *d = (pthread_data_t *)t_data;

    size_t local_byte_count = 0;
    do {
        pthread_mutex_lock(&d->input_lock);
        local_byte_count = fread(d->in_buf, sizeof(unsigned char),
                BUF_LENGTH_VALUE, stdin);

        d->n_bytes += local_byte_count;
        if (d->n_bytes > 0) { 
            fprintf(stdout, "PRODUCER ...signaling consumer...\n");
            pthread_cond_signal(&d->input_cond);
            fprintf(stdout, "PRODUCER ...consumer signaled...\n");
        }
        pthread_mutex_unlock(&d->input_lock);

        // This is added to slow down the producer so that we can observe
        // multiple consumptions.
        sleep(1);
    } while (local_byte_count >  0);



    return NULL;
}

void * consume_bytes(void *t_data) 
{
    pthread_data_t *d = (pthread_data_t *)t_data;
    while (1) {
        pthread_mutex_lock(&d->input_lock);
        while (d->n_bytes == 0) {
            fprintf(stdout, "CONSUMER entering wait \n");
            pthread_cond_wait(&d->input_cond, &d->input_lock);
        }
        fprintf(stdout, "CONSUMER ...consuming chunk...\n");
        d->n_bytes = 0;
        fprintf(stdout, "CONSUMER ...chunk consumed...\n");
        pthread_mutex_unlock(&d->input_lock);
        fflush(stdout);
    }

}

int main(){
    pthread_t producer_thread = NULL;
    pthread_t consumer_thread = NULL;
    pthread_data_t *thread_data = NULL;

    thread_data = malloc(sizeof(pthread_data_t));
    thread_data->n_bytes = 0;
    pthread_mutex_init(&(thread_data->input_lock), NULL);
    pthread_cond_init(&(thread_data->input_cond), NULL);

    pthread_create(&producer_thread, NULL, produce_bytes, (void *) thread_data);
    pthread_create(&consumer_thread, NULL, consume_bytes, (void *) thread_data);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
}