如何让C语言中生产者与消费者进程正常运行?
问题描述
我写了一段C语言的生产者消费者模型代码,整体能运行,但消费者进程无法正常工作。我尝试打印producer_pid排查问题,发现用printf("%d\n",producer_pid)会输出两个值,不加\n则只输出一个。怀疑生产者进程未正常结束,求解决方法。
原代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #define BUFFER_SIZE 2 int buffer[BUFFER_SIZE]; int sayac = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void produce(int item) { buffer[sayac] = item; sayac++; } int consume() { int item = buffer[sayac - 1]; sayac--; return item; } void *producer_thread(void *arg) { int *pipe_fd = (int *)arg; for (int i = 1; i <= 10; i++) { pthread_mutex_lock(&mutex); while (sayac == BUFFER_SIZE) { pthread_mutex_unlock(&mutex); usleep(100000); // 短暂等待释放循环 pthread_mutex_lock(&mutex); } produce(i); printf("生产者: %d 已生产。\n", i); write(pipe_fd[1], &i, sizeof(i)); pthread_mutex_unlock(&mutex); sleep(1); } close(pipe_fd[1]); return NULL; } void *consumer_thread(void *arg) { int *pipe_fd = (int *)arg; for (int i = 1; i <= 10; i++) { pthread_mutex_lock(&mutex); while (sayac == 0) { pthread_mutex_unlock(&mutex); usleep(100000); // 短暂等待释放循环 pthread_mutex_lock(&mutex); } int item = consume(); printf("消费者: %d 已消费。\n", item); int received_data; read(pipe_fd[0], &received_data, sizeof(received_data)); pthread_mutex_unlock(&mutex); sleep(1); } close(pipe_fd[0]); return NULL; } int main() { int pipe_fd[2]; if (pipe(pipe_fd) == -1) { perror("管道创建失败"); return 1; } pid_t producer_pid = fork(); printf("%d ---------------",producer_pid); if (producer_pid == 0) { // 子生产者进程代码 close(pipe_fd[0]); // 关闭读端 producer_thread((void *)pipe_fd); exit(0); } else if (producer_pid > 0) { pid_t consumer_pid = fork(); if (consumer_pid == 0) { // 子消费者进程代码 close(pipe_fd[1]); // 关闭写端 consumer_thread((void *)pipe_fd); exit(0); } else if (consumer_pid > 0) { // 主进程代码 close(pipe_fd[0]); // 关闭读端 close(pipe_fd[1]); // 关闭写端 wait(NULL); wait(NULL); printf("主进程已完成。\n"); } else { fprintf(stderr, "消费者进程创建失败。\n"); return 1; } } else { fprintf(stderr, "生产者进程创建失败。\n"); return 1; } return 0; }
问题分析与解决
1. printf输出两个值的原因
fork()调用后,父进程返回子进程的PID,子进程返回0,且子进程会复制父进程的所有内存(包括stdio的缓冲区)。
- 不加
\n时,printf的内容会留在缓冲区,父进程和子进程结束时都会刷新缓冲区,导致两次输出; - 加
\n时,printf会立即刷新缓冲区,父进程先输出,子进程的缓冲区为空,所以会看到父进程的PID和子进程的0两个值,这是正常现象,不是进程未结束的标志。
2. 消费者进程不工作的核心问题
- 进程间内存不共享:
fork()创建的子进程拥有独立内存空间,buffer、sayac、mutex都是各自进程的副本。生产者修改的是自己的缓冲区,消费者根本看不到,所以消费者会一直卡在sayac == 0的循环里。 - 错误混用线程函数与进程:代码里的
producer_thread、consumer_thread是为线程设计的,但你用了fork()创建进程,线程的互斥锁无法跨进程生效。 - 管道逻辑冗余且错位:消费者要先读到管道数据才能消费,但它先卡在等待缓冲区非空的循环,根本到不了
read步骤。
3. 解决方法
方法一:改用线程实现(推荐,适合生产者消费者模型)
线程共享同一进程的内存空间,全局变量、互斥锁可以直接生效,无需额外的进程间通信机制。修改后的代码如下:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #define BUFFER_SIZE 2 int buffer[BUFFER_SIZE]; int sayac = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 用条件变量替代轮询,更高效 pthread_cond_t not_full = PTHREAD_COND_INITIALIZER; pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER; void produce(int item) { buffer[sayac] = item; sayac++; } int consume() { int item = buffer[sayac - 1]; sayac--; return item; } void *producer_thread(void *arg) { for (int i = 1; i <= 10; i++) { pthread_mutex_lock(&mutex); // 等待缓冲区不满 while (sayac == BUFFER_SIZE) { pthread_cond_wait(¬_full, &mutex); } produce(i); printf("生产者: %d 已生产。\n", i); // 通知消费者缓冲区非空 pthread_cond_signal(¬_empty); pthread_mutex_unlock(&mutex); sleep(1); } return NULL; } void *consumer_thread(void *arg) { for (int i = 1; i <= 10; i++) { pthread_mutex_lock(&mutex); // 等待缓冲区非空 while (sayac == 0) { pthread_cond_wait(¬_empty, &mutex); } int item = consume(); printf("消费者: %d 已消费。\n", item); // 通知生产者缓冲区不满 pthread_cond_signal(¬_full); pthread_mutex_unlock(&mutex); sleep(1); } return NULL; } int main() { pthread_t producer_tid, consumer_tid; pthread_create(&producer_tid, NULL, producer_thread, NULL); pthread_create(&consumer_tid, NULL, consumer_thread, NULL); // 等待线程结束 pthread_join(producer_tid, NULL); pthread_join(consumer_tid, NULL); // 销毁同步对象 pthread_mutex_destroy(&mutex); pthread_cond_destroy(¬_full); pthread_cond_destroy(¬_empty); printf("主进程已完成。\n"); return 0; }
方法二:坚持用进程实现(需共享内存+进程间同步)
如果必须用进程,需要:
- 用
shmget/shmat创建共享内存,让buffer和sayac存储在共享内存中; - 用
pthread_mutexattr_setpshared将互斥锁设置为进程间共享,或用sem_init创建进程间信号量; - 调整管道逻辑(或直接用共享内存传递数据)。
内容的提问来源于stack exchange,提问作者0ptimus
相关产品推荐
相关产品推荐

