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(¬_full1, &mutex1); } if (next_char == '\n') { // 行结束 line_buffer[in1][i] = '\0'; in1 = (in1 + 1) % LINE_BUF_SIZE; i = 0; pthread_cond_signal(¬_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(¬_full1, &mutex1); } line_buffer[in1][0] = '\0'; in1 = (in1 + 1) % LINE_BUF_SIZE; pthread_cond_signal(¬_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(¬_empty1, &mutex1); } // 读到结束标记退出循环 if (line_buffer[out1][0] == '\0') { out1 = (out1 + 1) % LINE_BUF_SIZE; pthread_cond_signal(¬_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(¬_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(¬_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(¬_empty2); pthread_mutex_unlock(&mutex2); } l = r + 1; } r++; } lines++; } // 写入结束标记 pthread_mutex_lock(&mutex2); while ((in2 + 1) % WORD_BUF_SIZE == out2) { pthread_cond_wait(¬_full2, &mutex2); } word_buffer[in2][0] = '\0'; in2 = (in2 + 1) % WORD_BUF_SIZE; pthread_cond_signal(¬_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(¬_empty2, &mutex2); } // 读到结束标记退出 if (word_buffer[out2][0] == '\0') { out2 = (out2 + 1) % WORD_BUF_SIZE; pthread_cond_signal(¬_full2); pthread_mutex_unlock(&mutex2); break; } printf("%s\n\n", word_buffer[out2]); words++; out2 = (out2 + 1) % WORD_BUF_SIZE; pthread_cond_signal(¬_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
相关产品推荐
相关产品推荐

