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

Pthread生产者消费者作业:程序未输出全部文件单词、段错误或挂起排查

核心问题汇总
  • 局部变量未初始化:one函数内的下标变量i、two函数内的下标变量i声明后未赋值为0就直接使用,随机的初始值会导致数组越界访问,直接触发段错误。
  • 无同步机制导致竞态:所有共享索引(in1/out1/in2/out2)、共享缓冲区都是多线程并发读写的,仅用sched_yield无法保证操作原子性,会出现索引值被并发修改错乱,导致程序挂起、输出内容不全。
  • 单词拆分逻辑缺陷:仅遇到空格时才拆分单词,每行末尾的最后一个单词会直接丢失;连续空格会生成无效空单词写入缓冲区。
  • 缺少边界校验:未判断单行长度是否超过200、单个单词长度是否超过50,长输入会触发越界写内存。
修复后的完整代码
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

// 第一个共享缓冲区:存行,大小10
#define LINE_BUF_SIZE 10
#define LINE_MAX_LEN 200
char line_buffer[LINE_BUF_SIZE][LINE_MAX_LEN];
int in1 = 0;
int out1 = 0;

// 第二个共享缓冲区:存单词,大小20
#define WORD_BUF_SIZE 20
#define WORD_MAX_LEN 50
char word_buffer[WORD_BUF_SIZE][WORD_MAX_LEN];
int in2 = 0;
int out2 = 0;

// 同步锁和条件变量,对应两个缓冲区
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty1 = PTHREAD_COND_INITIALIZER;

pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full2 = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty2 = PTHREAD_COND_INITIALIZER;

// 第一个线程:读文件写行缓冲区
void * one(void *arg) {
  int file = *((int *)arg);
  char next_char;
  int n;
  int i = 0; // 初始化下标

  while ((n=read(file, &next_char, 1)) > 0) {
    pthread_mutex_lock(&mutex1);
    // 缓冲区满等待
    while ((in1 + 1) % LINE_BUF_SIZE == out1) {
      pthread_cond_wait(&not_full1, &mutex1);
    }
    
    if (next_char == '\n') { // 行结束
      line_buffer[in1][i] = '\0';
      in1 = (in1 + 1) % LINE_BUF_SIZE;
      i = 0;
      pthread_cond_signal(&not_empty1);
      pthread_mutex_unlock(&mutex1);
    } else {
      // 行长度越界保护
      if (i < LINE_MAX_LEN - 1) {
        line_buffer[in1][i++] = next_char;
      }
      pthread_mutex_unlock(&mutex1);
    }
  }

  // 写入结束标记
  pthread_mutex_lock(&mutex1);
  while ((in1 + 1) % LINE_BUF_SIZE == out1) {
    pthread_cond_wait(&not_full1, &mutex1);
  }
  line_buffer[in1][0] = '\0';
  in1 = (in1 + 1) % LINE_BUF_SIZE;
  pthread_cond_signal(&not_empty1);
  pthread_mutex_unlock(&mutex1);

  printf("Exit 1\n");
  pthread_exit(NULL);
}

// 第二个线程:读行拆单词,写单词缓冲区
void * two(void *arg) {
  int i;
  int l, r;
  int lines = 0;

  while (1) {
    pthread_mutex_lock(&mutex1);
    while (in1 == out1) {
      pthread_cond_wait(&not_empty1, &mutex1);
    }
    // 读到结束标记退出循环
    if (line_buffer[out1][0] == '\0') {
      out1 = (out1 + 1) % LINE_BUF_SIZE;
      pthread_cond_signal(&not_full1);
      pthread_mutex_unlock(&mutex1);
      break;
    }

    char *cur_line = line_buffer[out1];
    int line_len = strlen(cur_line);
    out1 = (out1 + 1) % LINE_BUF_SIZE;
    pthread_cond_signal(&not_full1);
    pthread_mutex_unlock(&mutex1);

    l = 0;
    r = 0;
    while (r <= line_len) {
      // 遇到空格或者行结束,拆分单词
      if (cur_line[r] == ' ' || cur_line[r] == '\0') {
        if (r > l) { // 跳过连续空格的空单词
          pthread_mutex_lock(&mutex2);
          while ((in2 + 1) % WORD_BUF_SIZE == out2) {
            pthread_cond_wait(&not_full2, &mutex2);
          }
          i = 0;
          while (l < r && i < WORD_MAX_LEN - 1) {
            word_buffer[in2][i++] = cur_line[l++];
          }
          word_buffer[in2][i] = '\0';
          in2 = (in2 + 1) % WORD_BUF_SIZE;
          pthread_cond_signal(&not_empty2);
          pthread_mutex_unlock(&mutex2);
        }
        l = r + 1;
      }
      r++;
    }
    lines++;
  }
  
  // 写入结束标记
  pthread_mutex_lock(&mutex2);
  while ((in2 + 1) % WORD_BUF_SIZE == out2) {
    pthread_cond_wait(&not_full2, &mutex2);
  }
  word_buffer[in2][0] = '\0';
  in2 = (in2 + 1) % WORD_BUF_SIZE;
  pthread_cond_signal(&not_empty2);
  pthread_mutex_unlock(&mutex2);

  printf("Exit 2\n");
  pthread_exit(NULL);
}

// 第三个线程:读单词统计输出
void * three(void *arg) {
  int words = 0;
  
  while (1) {
    pthread_mutex_lock(&mutex2);
    while (in2 == out2) {
      pthread_cond_wait(&not_empty2, &mutex2);
    }
    // 读到结束标记退出
    if (word_buffer[out2][0] == '\0') {
      out2 = (out2 + 1) % WORD_BUF_SIZE;
      pthread_cond_signal(&not_full2);
      pthread_mutex_unlock(&mutex2);
      break;
    }
    printf("%s\n\n", word_buffer[out2]);
    words++;
    out2 = (out2 + 1) % WORD_BUF_SIZE;
    pthread_cond_signal(&not_full2);
    pthread_mutex_unlock(&mutex2);
  }
  printf("Word count: %d\n", words);
  printf("Exit 3\n");
  pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
  if (argc != 2){
    write(STDERR_FILENO, "Usage: %s <input_file>\n", 24);
    exit(-1);
  }

  int file;
  if ((file = open(argv[1], O_RDONLY, 0)) == -1) {
    perror("Can't open file");
    exit(-1);
  }

  pthread_t tid_1, tid_2, tid_3;
  pthread_create(&tid_1, NULL, one, &file);
  pthread_create(&tid_2, NULL, two, NULL);
  pthread_create(&tid_3, NULL, three, NULL);

  pthread_join(tid_1, NULL);
  pthread_join(tid_2, NULL);
  pthread_join(tid_3, NULL);  

  close(file);
  printf("Exit main\n");
  return 0;
}
编译运行说明

编译时需要链接pthread库:
gcc producer_consumer.c -o pc -lpthread
运行时传入输入文件路径即可:
./pc test.txt

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:06:03