我正在使用pthreads中的生产者 - 消费者示例。这个想法如下。生成器生成一个新值n_consumers
,它在所有使用者线程之间共享。有k
个线程,只有一个生产者。为了便于访问n_consumers
,使用了具有k
元素的数组。这样,每次生成新的pool
时,都会将其复制到整个pool[0]=k; pool[1]=k;...; pool[n_consumers-1]=k
(void *consumer (void *args) {
int id = *(int *) args;
while (1) {
barrier (&barrier1, id);
// 1. lock
pthread_mutex_lock (&mu);
// 2. wait
pthread_cond_wait (&cond_producer_is_ready, &mu);
// 3. unlock
pthread_mutex_unlock (&mu);
// 4. do something with pool[id] value
printf ("thread %d using value %d\n", id, pool[id]);
// 5. stop?
if (stop_condition(pool[id]))
break;
}
return NULL;
}
void *producer (void *args) {
int i;
int id = n_consumers;
while (1) {
barrier(&barrier1, id);
// 1. lock
pthread_mutex_lock (&mu);
// 2. produce some new values
for (i=0; i<n_consumers; i++)
pool[i]++;
// 3. send message indicating a new value is available
printf ("producer sends broadcast...\n");
pthread_cond_broadcast (&cond_producer_is_ready);
// 4. unlock
pthread_mutex_unlock (&mu);
// 5. stop?
// it could be pool[x], it does not matter the index
if (stop_condition(pool[0]))
break;
}
return NULL;
}
)。
这是我的代码片段:
thread 0 in barrier (count 1)
thread 2 in barrier (count 2)
thread 1 in barrier (count 3)
thread 3 in barrier (count 4)
thread 4 in barrier (count 5) <- all the threads are in the barrier (OK)
producer sends broadcast... <- producer send the message to access to k
thread 4 in barrier (count 1) <- the producer waits in the barrier
thread 1 using value 1 <- consumer 1 received the message and use k=1
thread 1 in barrier (count 2)
thread 3 using value 1 <- consumer 2 and 3 received the message too
thread 3 in barrier (count 3)
thread 2 using value 1
thread 2 in barrier (count 4) <- only the consumer 0 did not received the message
这是输出:
cond_producer_is_ready
似乎只有少数消费者收到信号cond_producer_is_ready
,在这种情况下,消费者线程1,2和3,而消费者线程0仍然在等待这样的消息。屏障工作正常,因为所有线程都可以到达它。但问题是接收消息。是否可以确定给定线程收到或忽略给定的已发送消息(例如/*
| barrier_and_pool.c
| $ gcc barrier_and_pool.c -pthread -o barrier_and_pool.out
* */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
// shared resource
int *pool = NULL;
// producer and consumers
int n_consumers = 4;
int numproc = 5; // n_consumers + 1 producer
int new_value_available = 0; // mutex flag
int last_value_produced = 0; // mutex flag
pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_producer_is_ready = PTHREAD_COND_INITIALIZER;
typedef struct
{
int cur_count;
pthread_mutex_t barrier_mutex;
pthread_cond_t barrier_cond;
} barrier_t;
barrier_t pbarrier;
void barrier_init(barrier_t * mybarrier)
{
pthread_mutex_init(&(mybarrier->barrier_mutex), NULL);
pthread_cond_init(&(mybarrier->barrier_cond), NULL);
mybarrier->cur_count = 0;
}
void barrier(barrier_t * mybarrier, int id) {
pthread_mutex_lock(&(mybarrier->barrier_mutex));
mybarrier->cur_count++;
printf("thread %d in barrier (count %d)\n", id, mybarrier->cur_count);
if (mybarrier->cur_count!=numproc) {
pthread_cond_wait(&(mybarrier->barrier_cond), &(mybarrier->barrier_mutex));
}
else
{
mybarrier->cur_count=0;
printf ("broadcast sent by thread %d\n", id);
pthread_cond_broadcast(&(mybarrier->barrier_cond));
}
pthread_mutex_unlock(&(mybarrier->barrier_mutex));
printf ("thread %d out of barrier\n", id);
}
int stop_condition (int k_value) {
if (k_value >= 5) // we stop after five iters
return 1;
else
return 0;
}
void *consumer (void *args) {
int id = *(int *) args;
int last_value_consumed = 0;
while (1) {
barrier (&pbarrier, id);
// 1. lock
pthread_mutex_lock (&mu);
// 2. wait
while (last_value_produced == last_value_consumed)
pthread_cond_wait (&cond_producer_is_ready, &mu);
last_value_consumed++;
// 3. unlock
pthread_mutex_unlock (&mu);
// 4. do something with pool[id] value
printf ("thread %d using value %d\n", id, pool[id]);
// 5. stop?
if (stop_condition(pool[id]))
break;
}
return NULL;
}
void *producer (void *args) {
int i;
int id = n_consumers;
while (1) {
barrier(&pbarrier, id);
// 1. lock
pthread_mutex_lock (&mu);
// 2. produce some new values
for (i=0; i<n_consumers; i++)
pool[i]++;
// 3. send message indicating a new value is available
last_value_produced++;
printf ("producer sends broadcast (last_value_produced: %d)...\n", last_value_produced);
pthread_cond_broadcast (&cond_producer_is_ready);
// 4. unlock
pthread_mutex_unlock (&mu);
// 5. stop?
// it could be pool[x], it does not matter the index
if (stop_condition(pool[0]))
break;
}
return NULL;
}
int main (int argc, char *argv[]) {
pool = (int*) malloc (sizeof(int)*n_consumers);
int *ids = (int*) malloc(sizeof(int)*n_consumers);
pthread_t pro;
pthread_t cons[n_consumers];
// init barrier
barrier_init(&pbarrier);
// init consumers
int i;
for (i=0; i<n_consumers; i++) {
ids[i] = i;
pool[i] = 0;
pthread_create (&cons[i], NULL, consumer, &ids[i]);
}
// init producer
pthread_create (&pro, NULL, producer, NULL);
// join
for (i=0; i<n_consumers; i++) {
pthread_join (cons[i], NULL);
}
pthread_join (pro, NULL);
return 0;
}
)?
更新 感谢@caf的回答。这是我原始代码的更正版本,没有上一个错误,只是为了完整性(或从pastebin复制):
{{1}}
答案 0 :(得分:2)
您的代码的问题在于,生产者可以在任何或所有消费者之前获取互斥锁,在这种情况下,当条件发出信号时,消费者将在pthread_mutex_lock();
等待 - 所以他们然后将永远等待pthread_cond_wait()
(信号没有排队:如果你没有等待条件变量发出信号,你就会错过它。)
这就是为什么pthread条件变量必须与某个共享状态的条件配对 - 称为谓词。而不是仅仅调用pthread_cond_wait()
,而是在一个测试谓词的循环中调用它:
while (!new_value_available)
pthread_cond_wait (&cond_producer_is_ready, &mu);
这种方式无论生产者是先到达还是消费者都没关系:如果消费者在生产者之前到达其关键部分,则谓词将为假,消费者将等待;如果生产者在消费者之前到达其关键部分,则条件为真,消费者将继续。
在这种情况下,要创建谓词,您可以将全局共享变量last_value_produced
初始化为零,并在广播条件变量之前由生产者递增。每个使用者维护一个局部变量last_value_consumed
也初始化为零,条件变为:
while (last_value_consumed == last_value_produced)
pthread_cond_wait(&cond_producer_is_ready, &mu);
然后消费者递增last_value_consumed
。