基于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; }
关键修复点说明
- 线程参数结构体:新增
ThreadArgs结构体,将Monitor指针和线程ID打包,符合pthread_create的参数要求。 - 统一使用Monitor内部变量:删除全局的
buffer、last等变量,所有操作都基于Monitor结构体内部的成员,保证数据一致性。 - 添加退出标志:在Monitor中加入
is_running,生产者全部结束后设置为0,唤醒所有消费者线程退出循环。 - 修正随机数种子初始化:将
srand移到main函数中,避免多个线程重复初始化导致随机数重复。 - 修正printBuffer函数:改为接收Monitor内部的buffer作为参数,输出正确的缓冲区状态。
内容的提问来源于stack exchange,提问作者NX27
相关产品推荐
相关产品推荐

