You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

条件变量正确使用方法及生产者消费者模型无输出问题排查

条件变量正确用法及你的代码问题修复

条件变量核心使用原则

  • 必须与互斥锁绑定使用:调用pthread_cond_wait前必须持有锁,函数执行时会自动释放锁,被唤醒后会重新获取锁,无需手动解锁。
  • 用while循环检查等待条件:防止虚假唤醒(系统可能无理由唤醒线程),或者唤醒后条件已不满足(比如多消费者场景下其他线程已处理了数据)。
  • 信号要发给对应等待的线程:生产者生产完后,发信号通知消费者“有数据了”;消费者消费完后,发信号通知生产者“有空间了”。

你的代码里的关键错误

  1. 条件变量完全用反了:你让生产者等待自己的cond1、消费者等待自己的cond2,相当于自己等自己发信号,完全起不到线程间同步的作用。
  2. pthread_cond_wait后手动解锁:pthread_cond_wait返回时已经重新持有了互斥锁,此时再解锁会导致重复解锁,引发线程库状态混乱,直接导致死锁。
  3. 用if判断等待条件:没有处理虚假唤醒,且多线程场景下条件可能已变化,必须用while循环重新检查。
  4. 外层轮询循环多余:条件变量的作用就是避免轮询,你保留的外层while轮询完全没必要,还会逻辑混乱。
  5. 信号发送时机错误:你在进入临界区时就发信号,而不是在修改完共享数据后发,导致信号无效。
  6. 未初始化共享变量:shared_count没有初始化为0,初始值不确定会直接破坏条件判断逻辑。

修正后的完整代码

/* minor3.c - using producer-consumer paradigm. */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NITEMS 10       // number of items in shared buffer

// shared variables
char shared_buffer[NITEMS]; // echo buffer
int shared_count = 0;       // 必须初始化,否则初始值不确定

pthread_mutex_t mutex;      // pthread mutex
unsigned int prod_index = 0;    // producer index into shared buffer
unsigned int cons_index = 0;    // consumer index into shard buffer

// function prototypes
void * producer(void *arg);
void * consumer(void *arg);

// 明确条件变量作用:通知消费者有数据、通知生产者有空间
pthread_cond_t cond_not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_not_full = PTHREAD_COND_INITIALIZER;

int main()
{
    pthread_t prod_tid, cons_tid1, cons_tid2;

    // initialize pthread variables
    pthread_mutex_init(&mutex, NULL);

    // initialize condition variables
    pthread_cond_init(&cond_not_empty, NULL);
    pthread_cond_init(&cond_not_full, NULL);

    // start producer thread
    pthread_create(&prod_tid, NULL, producer, NULL);

    // start consumer threads
    pthread_create(&cons_tid1, NULL, consumer, NULL);
    pthread_create(&cons_tid2, NULL, consumer, NULL);

    // wait for threads to finish
    pthread_join(prod_tid, NULL);
    pthread_join(cons_tid1, NULL);
    pthread_join(cons_tid2, NULL);

    // clean up
    pthread_mutex_destroy(&mutex);

    pthread_cond_destroy(&cond_not_empty);
    pthread_cond_destroy(&cond_not_full);

    return 0;
}

// producer thread executes this function
void * producer(void *arg)
{
    char key;

    printf("Enter text for producer to read and consumer to print, use Ctrl-C to exit.\n\n");

    while (1)
    {
        // scanf会阻塞等待输入,这部分无需加锁
        scanf("%c", &key);

        // 获取锁后操作共享资源
        pthread_mutex_lock(&mutex);

        // 用while循环等待缓冲区非满,处理虚假唤醒
        while (shared_count == NITEMS) {
            // 等待消费者发的"有空间"信号
            pthread_cond_wait(&cond_not_full, &mutex);
        }

        // 写入数据到缓冲区
        shared_buffer[prod_index] = key;
        shared_count++;

        // 更新生产者索引
        prod_index = (prod_index == NITEMS - 1) ? 0 : prod_index + 1;

        // 通知消费者:缓冲区有数据了
        pthread_cond_signal(&cond_not_empty);

        // 释放锁
        pthread_mutex_unlock(&mutex); 
    }

    return NULL;
}

// consumer thread executes this function
void * consumer(void *arg)
{
    char key;
    long unsigned int id = (long unsigned int)pthread_self();

    while (1)
    {
        pthread_mutex_lock(&mutex);

        // 用while循环等待缓冲区非空,处理虚假唤醒
        while (shared_count == 0) {
            // 等待生产者发的"有数据"信号
            pthread_cond_wait(&cond_not_empty, &mutex);
        }

        // 从缓冲区读取数据
        key = shared_buffer[cons_index];
        printf("consumer %lu: %c\n", id, key);
        shared_count--;

        // 更新消费者索引
        cons_index = (cons_index == NITEMS - 1) ? 0 : cons_index + 1;

        // 通知生产者:缓冲区有空间了
        pthread_cond_signal(&cond_not_full);

        // 释放锁
        pthread_mutex_unlock(&mutex);
    }

    return NULL;
}

关键修改说明

  1. 初始化shared_count:明确初始值为0,保证条件判断的正确性。
  2. 重命名条件变量:用cond_not_empty和cond_not_full明确分工,避免混淆。
  3. 移除多余轮询循环:直接在持有锁的状态下用while循环等待条件,彻底消除轮询导致的高CPU占用。
  4. 修正wait后的解锁逻辑:去掉pthread_cond_wait后的手动解锁,避免重复解锁引发的死锁。
  5. 调整信号发送时机:在修改完共享数据后发送信号,确保信号能准确传递有效状态。
  6. 用while循环检查条件:处理虚假唤醒和多线程竞争场景下的条件变化。

内容的提问来源于stack exchange,提问作者Samuel Afon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:14:53