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

如何让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(&not_full, &mutex);
        }

        produce(i);
        printf("生产者: %d 已生产。\n", i);

        // 通知消费者缓冲区非空
        pthread_cond_signal(&not_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(&not_empty, &mutex);
        }

        int item = consume();
        printf("消费者: %d 已消费。\n", item);

        // 通知生产者缓冲区不满
        pthread_cond_signal(&not_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(&not_full);
    pthread_cond_destroy(&not_empty);

    printf("主进程已完成。\n");

    return 0;
}

方法二:坚持用进程实现(需共享内存+进程间同步)

如果必须用进程,需要:

  1. 用shmget/shmat创建共享内存,让buffer和sayac存储在共享内存中;
  2. 用pthread_mutexattr_setpshared将互斥锁设置为进程间共享,或用sem_init创建进程间信号量;
  3. 调整管道逻辑(或直接用共享内存传递数据)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:50:27