协助对 pthread_cond_wait() 的错误理解
Assistance with flawed understanding of pthread_cond_wait()
我显然对条件变量及其使用方法的理解存在缺陷。我的意图是拥有一个生产者和多个消费者线程,但我可以用一个生产者和一个消费者来证明我的问题。
有一个名为 work
的共享变量受互斥锁和条件变量的保护。生产者设置 work
变量并发出准备就绪的信号,但消费者线程永远不会到达 运行?
如果程序正常运行,它应该打印 this line never get's printed
,但我却得到 consumer never did work...giving up
。如有任何帮助,我们将不胜感激
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int work = 0;
void* consumer(void* ptr) {
pthread_mutex_lock(&mutex);
while(1) {
pthread_cond_wait(&cond, &mutex);
if (work == 0)
continue;
printf("this line never get's printed\n");
work = 0;
}
return NULL;
}
int main() {
pthread_t thr;
pthread_create(&thr, NULL, consumer, NULL);
sleep(1); /* give consumer moment to lock mutex */
for(int ndx=0; ndx < 50; ndx++) {
pthread_mutex_lock(&mutex);
if (work == 1) {
printf("consumer never did work...giving up\n");
return -1;
}
work = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return 0;
}
编译:
$ g++ -pthread simple.cpp -o simple
运行 在 Debian 7.9 上(也在 CentOS 6.7 上转载)
$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.13
$ g++ --version
gcc version 4.7.2 (Debian 4.7.2-5)
您的问题是,尽管您的消费者线程在继续之前等待生产者生产,但生产者不会同样等待消费者消费。仅仅因为生产者调用 pthread_cond_signal()
并不意味着等待该条件变量的线程将被立即调度,特别是因为生产者线程在调用时锁定了关联的互斥锁。消费者在锁定互斥锁之前无法恢复,并且完全有可能虽然生产者线程解锁它,但它也会循环循环并在消费者继续之前再次锁定它。
您可以使用相同的条件变量或与相同的互斥量关联的另一个条件变量,以允许消费者向生产者发出可以继续的信号。
总结我的意见:
1.线程之间共享的数据应该(阅读:必须)始终声明为 volatile
;在你的情况下应该是 volatile int work = 0;
.
(已删除,见评论)
pthread_cond_signal
和 pthread_mutex_unlock
都不保证 何时 任何等待线程继续。因此,消费者很可能没有机会在生产者的 work = 1;
和 if (work == 1)
之间 运行。
我显然对条件变量及其使用方法的理解存在缺陷。我的意图是拥有一个生产者和多个消费者线程,但我可以用一个生产者和一个消费者来证明我的问题。
有一个名为 work
的共享变量受互斥锁和条件变量的保护。生产者设置 work
变量并发出准备就绪的信号,但消费者线程永远不会到达 运行?
如果程序正常运行,它应该打印 this line never get's printed
,但我却得到 consumer never did work...giving up
。如有任何帮助,我们将不胜感激
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int work = 0;
void* consumer(void* ptr) {
pthread_mutex_lock(&mutex);
while(1) {
pthread_cond_wait(&cond, &mutex);
if (work == 0)
continue;
printf("this line never get's printed\n");
work = 0;
}
return NULL;
}
int main() {
pthread_t thr;
pthread_create(&thr, NULL, consumer, NULL);
sleep(1); /* give consumer moment to lock mutex */
for(int ndx=0; ndx < 50; ndx++) {
pthread_mutex_lock(&mutex);
if (work == 1) {
printf("consumer never did work...giving up\n");
return -1;
}
work = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return 0;
}
编译:
$ g++ -pthread simple.cpp -o simple
运行 在 Debian 7.9 上(也在 CentOS 6.7 上转载)
$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.13
$ g++ --version
gcc version 4.7.2 (Debian 4.7.2-5)
您的问题是,尽管您的消费者线程在继续之前等待生产者生产,但生产者不会同样等待消费者消费。仅仅因为生产者调用 pthread_cond_signal()
并不意味着等待该条件变量的线程将被立即调度,特别是因为生产者线程在调用时锁定了关联的互斥锁。消费者在锁定互斥锁之前无法恢复,并且完全有可能虽然生产者线程解锁它,但它也会循环循环并在消费者继续之前再次锁定它。
您可以使用相同的条件变量或与相同的互斥量关联的另一个条件变量,以允许消费者向生产者发出可以继续的信号。
总结我的意见:
1.线程之间共享的数据应该(阅读:必须)始终声明为
(已删除,见评论)volatile
;在你的情况下应该是 volatile int work = 0;
.
pthread_cond_signal
和pthread_mutex_unlock
都不保证 何时 任何等待线程继续。因此,消费者很可能没有机会在生产者的work = 1;
和if (work == 1)
之间 运行。