C多线程文件读取问题求助:单线程处理一行且无重复读取
咱来看看你这段代码为啥没输出,问题还挺多的,我给你逐个拆解,再给你改好的版本:
问题分析
1. 函数嵌套定义(语法错误)
C标准不允许在函数内部定义其他函数,你把getLineCount和readLine都塞在main里了,很多编译器直接会报错,根本编译不过,这是第一个大问题。
2. 线程参数传递的致命bug
你在循环里把&i传给线程,但i是循环变量,所有线程共享这个地址。线程启动是异步的,等线程真正开始执行的时候,循环可能已经跑完了,所有线程拿到的lineN都是最后一个i的值,根本没法正确处理对应行。
3. 互斥锁的错误操作
- 你只声明了
pthread_mutex_t lock;但没初始化,应该调用pthread_mutex_init(&lock, NULL);才能正常使用锁。 - 你在循环里创建一个线程就调用
pthread_mutex_destroy(&lock);,这直接把锁销毁了,后续线程调用pthread_mutex_lock会直接出错,完全起不到互斥的作用。
4. 文件读取逻辑错误
每个线程都重新打开文件,逐行读到目标行,这效率极低。而且你用fgets读行之后,又调用fscanf,这时候fscanf会从fgets读过的下一行开始读,导致你读的数据根本不是目标行的内容,就算有输出也是错的。
5. 线程join的硬编码问题
你手动写了5个pthread_join,如果data.txt的行数不是5,要么有线程没被join(程序可能提前退出),要么访问tid数组越界(直接崩溃)。
6. 全局变量的不合理使用
a和b是全局变量,虽然有锁,但完全没必要,直接在函数里用局部变量就行,避免不必要的共享。
修正后的代码
我把这些问题都修复了,还优化了逻辑:让主线程先把所有行读进内存,然后每个线程处理对应索引的行,这样不用每个线程都打开文件,效率高很多。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<pthread.h> // 全局结构体,用来给线程传递参数 typedef struct { int line_idx; char* line_content; } ThreadData; // 计算文件行数(移到main外面) int getLineCount(const char* filename) { FILE *fp = fopen(filename, "r"); if(fp == NULL) { printf("File does not exist!!!\n"); return -1; } char ch; int linesCount = 0; // 处理最后一行没有换行符的情况 int has_content = 0; while((ch = fgetc(fp)) != EOF) { has_content = 1; if(ch == '\n') { linesCount++; } } // 如果文件有内容且最后一行没换行,行数+1 if(has_content && ch != '\n') { linesCount++; } fclose(fp); return linesCount; } // 线程处理函数(移到main外面) void *readLine(void *arg) { ThreadData* data = (ThreadData*)arg; int a, b; // 从行内容里解析整数 if(sscanf(data->line_content, "%d %d", &a, &b) == 2) { printf("Thread %d: %d + %d = %d\n", data->line_idx, a, b, a + b); } else { printf("Thread %d: Invalid line content\n", data->line_idx); } // 释放线程参数的内存 free(data); pthread_exit(NULL); } int main() { const char* filename = "data.txt"; int lineCount = getLineCount(filename); if(lineCount <= 0) { return 1; } // 先把所有行读进内存 char** lines = (char**)malloc(lineCount * sizeof(char*)); if(lines == NULL) { printf("Memory allocation failed!\n"); return 1; } FILE* fp = fopen(filename, "r"); if(fp == NULL) { printf("File does not exist!!!\n"); free(lines); return 1; } char buffer[256]; int i = 0; while(fgets(buffer, sizeof(buffer), fp) != NULL) { // 去掉换行符 buffer[strcspn(buffer, "\n")] = '\0'; lines[i] = (char*)malloc(strlen(buffer) + 1); strcpy(lines[i], buffer); i++; } fclose(fp); pthread_t tid[lineCount]; for(i = 0; i < lineCount; i++) { // 为每个线程分配参数内存 ThreadData* data = (ThreadData*)malloc(sizeof(ThreadData)); data->line_idx = i + 1; // 行号从1开始 data->line_content = lines[i]; // 创建线程 if(pthread_create(&tid[i], NULL, readLine, (void*)data) != 0) { printf("Failed to create thread %d\n", i + 1); free(data); // 出错了要释放已分配的内存 for(int j = 0; j < i; j++) { pthread_join(tid[j], NULL); free(lines[j]); } free(lines); return 1; } } // 等待所有线程完成 for(i = 0; i < lineCount; i++) { pthread_join(tid[i], NULL); free(lines[i]); } free(lines); return 0; }
关键优化点
- 把函数移到
main外面,符合C标准语法。 - 主线程提前读取所有行到内存,每个线程直接处理对应行,避免重复打开文件,效率更高。
- 用结构体传递线程参数,每个线程有独立的参数内存,避免循环变量地址的问题。
- 动态join所有线程,不管文件有多少行都能正确处理。
- 去掉了不必要的互斥锁,因为线程之间不再共享文件操作,只处理自己的行数据。
内容的提问来源于stack exchange,提问作者Natalie112
相关产品推荐
相关产品推荐

