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

基于C语言的生产者/消费者问题中Monitor实现的线程异常排查

问题排查与修复

你的代码核心问题集中在线程函数签名不匹配、全局变量与Monitor内部变量混用这两点,导致线程执行逻辑混乱,消费者线程无法正常工作。以下是具体错误分析和修复方案:

1. 线程函数签名不符合pthread_create要求

pthread_create要求线程函数的签名必须是:

void* (*thread_func)(void*)

但你的produce和consume函数参数顺序是(Monitor*, void*),完全不符合要求。pthread只能传递一个void*参数,因此需要把Monitor指针和线程ID打包成一个结构体,作为参数传递给线程函数。

2. 全局变量与Monitor内部变量混用

你同时定义了全局的buffer、last、mutex等,又在Monitor结构体里重复定义了这些变量,代码中produce函数使用全局的last和buffer,而consume函数又混用monitor->last,导致数据状态不一致,消费者线程永远检测不到有数据可以消费。

3. 其他次要问题

  • srand(time(NULL))放在produce循环内,多个线程同时调用会导致随机数重复,应该在main函数初始化时调用一次。
  • 消费者线程的while(1)没有退出条件,生产者结束后会一直阻塞在join阶段。
  • printBuffer函数没有使用Monitor内部的buffer,导致输出错误。

修复后的完整代码

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

#define NUM_PROD 4
#define NUM_CONS 4
#define STORAGE_SPACE 3

// 线程参数结构体:打包Monitor指针和线程ID
typedef struct ThreadArgs {
    struct Monitor* monitor;
    long thread_id;
} ThreadArgs;

typedef struct Monitor {
    pthread_mutex_t mutex;
    pthread_cond_t can_produce;
    pthread_cond_t can_consume;

    int buffer[STORAGE_SPACE];
    int last;
    int is_running; // 结束标志,通知消费者退出
} Monitor; 

void printBuffer(int buffer[], int size) {
    int i = 0;
    printf("Buffer state: ");
    for (i = 0; i < size; i++) {
        printf("%d ", buffer[i]);
    }
    printf("\n");
}

void initMonitor(Monitor* monitor){
    monitor->last = 0;
    monitor->is_running = 1; // 初始为运行状态

    pthread_mutex_init(&monitor->mutex, NULL);
    pthread_cond_init(&monitor->can_produce, NULL);
    pthread_cond_init(&monitor->can_consume, NULL);
}

void *produce(void* arg){
    ThreadArgs* args = (ThreadArgs*)arg;
    Monitor* monitor = args->monitor;
    long tid = args->thread_id;
    int i, item;

    for( i = 0; i < 5; i ++){
        pthread_mutex_lock(&monitor->mutex);
        // 等待缓冲区有空位
        while(monitor->last == STORAGE_SPACE){
            pthread_cond_wait(&monitor->can_produce, &monitor->mutex);
        }
        item = rand() % 50;
        monitor->buffer[monitor->last] = item;
        printf("prod %ld inserts item %d\n", tid, monitor->buffer[monitor->last]);
        monitor->last++;
        printBuffer(monitor->buffer, STORAGE_SPACE);

        pthread_cond_signal(&monitor->can_consume);
        pthread_mutex_unlock(&monitor->mutex);
        usleep(100000); // 模拟生产耗时
    }
    free(args); // 释放参数内存
    pthread_exit(NULL);
}

void *consume(void* arg){
    ThreadArgs* args = (ThreadArgs*)arg;
    Monitor* monitor = args->monitor;
    long tid = args->thread_id;
    int taken;

    while(1){
        pthread_mutex_lock(&monitor->mutex);
        // 等待缓冲区有数据,同时检查是否需要退出
        while(monitor->last == 0 && monitor->is_running){
            pthread_cond_wait(&monitor->can_consume, &monitor->mutex);
        }
        // 如果没有数据且生产者已全部结束,退出循环
        if(monitor->last == 0 && !monitor->is_running){
            pthread_mutex_unlock(&monitor->mutex);
            break;
        }
        taken = monitor->buffer[monitor->last - 1];
        monitor->buffer[monitor->last - 1] = 0;
        printf("consumer %ld took %d\n", tid, taken);
        monitor->last--;
        printBuffer(monitor->buffer, STORAGE_SPACE);

        pthread_cond_signal(&monitor->can_produce);
        pthread_mutex_unlock(&monitor->mutex);
        usleep(150000); // 模拟消费耗时
    }
    free(args);
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    Monitor monitor; 
    initMonitor(&monitor);
    pthread_t prod[NUM_PROD], cons[NUM_CONS];
    int rc;
    long t;

    srand(time(NULL)); // 全局初始化随机数种子

    // 创建生产者线程
    for (t = 0; t < NUM_PROD; t++)
    {
        ThreadArgs* args = malloc(sizeof(ThreadArgs));
        args->monitor = &monitor;
        args->thread_id = t;
        rc = pthread_create(&prod[t], NULL, produce, (void*)args);
        if (rc)
        {
            perror("pthread_create producer failed");
            exit(-1);
        }
    }

    // 创建消费者线程
    for (t = 0; t < NUM_CONS; t++)
    {
        ThreadArgs* args = malloc(sizeof(ThreadArgs));
        args->monitor = &monitor;
        args->thread_id = t;
        rc = pthread_create(&cons[t], NULL, consume, (void*)args);
        if (rc)
        {
            perror("pthread_create consumer failed");
            exit(-1);
        }
    }

    // 等待所有生产者线程结束
    for (t = 0; t < NUM_PROD; t++)
    {
        pthread_join(prod[t], NULL);
    }

    // 通知消费者线程退出
    pthread_mutex_lock(&monitor.mutex);
    monitor.is_running = 0;
    pthread_cond_broadcast(&monitor.can_consume); // 唤醒所有等待的消费者
    pthread_mutex_unlock(&monitor.mutex);

    // 等待所有消费者线程结束
    for (t = 0; t < NUM_CONS; t++)
    {
        pthread_join(cons[t], NULL);
    }

    // 销毁同步对象
    pthread_mutex_destroy(&monitor.mutex);
    pthread_cond_destroy(&monitor.can_produce);
    pthread_cond_destroy(&monitor.can_consume);

    return 0;
}

关键修复点说明

  1. 线程参数结构体:新增ThreadArgs结构体,将Monitor指针和线程ID打包,符合pthread_create的参数要求。
  2. 统一使用Monitor内部变量:删除全局的buffer、last等变量,所有操作都基于Monitor结构体内部的成员,保证数据一致性。
  3. 添加退出标志:在Monitor中加入is_running,生产者全部结束后设置为0,唤醒所有消费者线程退出循环。
  4. 修正随机数种子初始化:将srand移到main函数中,避免多个线程重复初始化导致随机数重复。
  5. 修正printBuffer函数:改为接收Monitor内部的buffer作为参数,输出正确的缓冲区状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:17:02