【问题标题】:Assistance with flawed understanding of pthread_cond_wait()协助对 pthread_cond_wait() 的错误理解
【发布时间】:2015-09-15 14:02:08
【问题描述】:

我对条件变量以及如何使用它们的理解显然存在缺陷。我的意图是拥有一个生产者和多个消费者线程,但我可以用一个生产者和一个消费者来证明我的问题。

有一个名为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)

【问题讨论】:

  • 您的代码是 C,而不是 C++。建议使用 C 编译器(例如 gcc)而不是 C++ 编译器来编译它,无论如何我已经删除了你的 c++ 标签。
  • 不要忽略检查函数调用的返回值。如果发生错误,您需要能够做出适当的响应,即使这只是干净地终止程序。
  • 建议:阅读来自 LLNL 的Pthread Tutorial
  • volatile int work = 0;
  • @JohnBollinger 如果 OP 使用 C++ 编译器,则代码为 C++。

标签: c++ multithreading pthreads mutex


【解决方案1】:

您的问题是,尽管您的消费者线程在继续之前等待生产者生产,但生产者并不同样等待消费者消费。仅仅因为生产者调用pthread_cond_signal() 并不意味着将立即调度等待该条件变量的线程,尤其是因为生产者线程在调用时已锁定关联的互斥锁。消费者在锁定互斥体之前无法恢复,而且完全合理的是,虽然生产者线程解锁了它,但它也会循环循环并在消费者继续之前再次锁定它。

您可以使用相同的条件变量或与相同互斥锁关联的另一个条件变量,以允许消费者向生产者发出可以继续进行的信号。

【讨论】:

    【解决方案2】:

    总结我的cmets:

    1。线程之间共享的数据应该(阅读:必须)始终声明为volatile;在你的情况下,它应该是volatile int work = 0;。 (已移除,见 cmets)

    1. pthread_cond_signalpthread_mutex_unlock 都不保证何时任何等待线程继续。因此很可能消费者没有机会在生产者的work = 1;if (work == 1) 之间运行。

    【讨论】:

    • 但是,for 循环中的pthread_yield() 不能保证线程有机会运行吗?我试过了——还是不行。
    • 不,pthread_yield() 也不做任何此类保证。它基本上由操作系统自行决定下一个从哪个进程运行哪个线程,或者只是返回到调用线程。
    • 如果在互斥锁仍然锁定的情况下让步,那么生产者线程将再次被调度,因为它是唯一可以取得进展的线程。
    • 然而,我不得不对声明共享数据volatile 的建议提出异议。有关原因的广泛讨论,请参阅 this question 及其答案。
    • 我在stackoverflow.com/a/24138417/1015327中搜索发现pthread_mutex_lock已经包含了一个(编译器可见的)内存屏障,所以volatile在这里真的是多余的。
    猜你喜欢
    • 2013-05-07
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 2015-09-16
    • 2023-03-21
    相关资源
    最近更新 更多