我在使用基本的双线程安排时遇到了一些麻烦。
我正在一个"生产者"中从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作为我尝试做的事情的基础。我做错了什么?
答案 0 :(得分:1)
该代码存在一些问题:
produce_bytes
会在阻止调用fread
期间锁定互斥锁。响应式应用程序的一般经验法则是尽可能短时间锁定互斥锁。您可能希望首先将输入读入临时缓冲区,然后锁定互斥锁并将数据复制到线程之间共享的缓冲区。同样适用于consume_bytes
,它在调用可以阻止的fprintf
时保留互斥锁。produce_bytes
中的while(d->n_bytes > 0)
不会保留互斥锁,这是一种竞争条件,因为consume_bytes
会为d->n_bytes
分配一个新值。假设您希望在fread
返回0(EOF)时退出该循环,则需要将fread
的返回值复制到线程之间未共享的局部变量中,并将其用作{while(read_bytes > 0)
中的条件。 1}} 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);
}