pthreads有条件 - 有可能忽略cond信号?

时间:2016-02-05 03:55:50

标签: pthreads

我正在使用pthreads中的生产者 - 消费者示例。这个想法如下。生成器生成一个新值n_consumers,它在所有使用者线程之间共享。有k个线程,只有一个生产者。为了便于访问n_consumers,使用了具有k元素的数组。这样,每次生成新的pool时,都会将其复制到整个pool[0]=k; pool[1]=k;...; pool[n_consumers-1]=kvoid *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}}

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