运行多线程C程序触发段错误的原因排查及代码求助
问题描述
编写了一个包含两个线程的C程序:线程A负责读取文件内容并共享给线程B,线程B接收数据后统计文件中的字节数量。程序使用信号量实现线程间通信,两个线程能正常运行,但程序即将终止时触发内存段错误。
原代码
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #define BUFFER_SIZE 4096 typedef struct _Buffer { int size; char data[BUFFER_SIZE]; } Buffer; sem_t task1, task2; void *thread_A(void *arg); void *thread_B(void *arg); int main(int argc, char *argv[]) { Buffer *memory = malloc(sizeof(Buffer)); sem_init(&task1, 0, 0); sem_init(&task2, 0, 0); pthread_t thread_A_id; pthread_t thread_B_id; pthread_create(&thread_A_id, NULL, &thread_A, &memory); pthread_create(&thread_B_id, NULL, &thread_B, &memory); if (pthread_join(thread_A_id, NULL) != 0) { perror("Error joining thread A"); exit(1); } if (pthread_join(thread_B_id, NULL) != 0) { perror("Error joining thread B"); exit(1); } free(memory); return 0; } void *thread_A(void *arg) { Buffer *buffer = (Buffer*) arg; FILE *pdf_file = fopen("file.pdf", "rb"); if (pdf_file == NULL) { perror("Can not open the file"); } printf("size of struct %ld\n", sizeof(Buffer)); buffer->size = fread(&buffer->data, sizeof(char), BUFFER_SIZE, pdf_file); fclose(pdf_file); sem_post(&task1); sem_wait(&task2); printf("A is out\n"); return NULL; } void *thread_B(void *arg) { printf("IAM IN TREAD B\n"); Buffer *buffer = (Buffer*) arg; sem_wait(&task1); int i=0; int byte_counts[256] = {0}; while (buffer->size != i) { unsigned char byte = buffer->data[i]; byte_counts[byte]++; i++; } for (int i = 0; i < 256; i++) { printf("Byte-value %02X: %d\n", i, byte_counts[i]); } sem_post(&task2); printf("threadB is done 2\n"); return NULL; }
错误原因及修复方案
核心错误:指针类型不匹配
main函数中创建线程时,传递的参数是&memory(指向Buffer指针的指针,即Buffer**类型),但线程函数里直接将其强转为Buffer*,导致后续访问buffer->size或buffer->data时,实际访问的是错误内存地址,最终触发段错误。
修复:创建线程时直接传递memory(Buffer*类型),而非&memory。
次要问题及修复
- 文件打开失败后未终止线程:若文件打开失败,后续的
fread、fclose会访问空指针,需在perror后退出线程,同时触发信号量避免线程B无限等待。 - fread参数错误:原代码中
&buffer->data多余,数组名本身就是首地址,传递buffer->data即可。 - 冗余语法问题:thread_B中
int i=0;;多了一个分号,已去除。
修复后的完整代码
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #define BUFFER_SIZE 4096 typedef struct _Buffer { int size; char data[BUFFER_SIZE]; } Buffer; sem_t task1, task2; void *thread_A(void *arg); void *thread_B(void *arg); int main(int argc, char *argv[]) { Buffer *memory = malloc(sizeof(Buffer)); if (!memory) { perror("malloc failed"); exit(1); } sem_init(&task1, 0, 0); sem_init(&task2, 0, 0); pthread_t thread_A_id; pthread_t thread_B_id; // 修复:传递正确的Buffer*类型参数 pthread_create(&thread_A_id, NULL, &thread_A, memory); pthread_create(&thread_B_id, NULL, &thread_B, memory); if (pthread_join(thread_A_id, NULL) != 0) { perror("Error joining thread A"); exit(1); } if (pthread_join(thread_B_id, NULL) != 0) { perror("Error joining thread B"); exit(1); } sem_destroy(&task1); sem_destroy(&task2); free(memory); return 0; } void *thread_A(void *arg) { Buffer *buffer = (Buffer*) arg; FILE *pdf_file = fopen("file.pdf", "rb"); if (pdf_file == NULL) { perror("Can not open the file"); sem_post(&task1); // 触发信号量,避免线程B阻塞 sem_wait(&task2); return NULL; } printf("size of struct %ld\n", sizeof(Buffer)); // 修复:传递数组首地址给fread buffer->size = fread(buffer->data, sizeof(char), BUFFER_SIZE, pdf_file); fclose(pdf_file); sem_post(&task1); sem_wait(&task2); printf("A is out\n"); return NULL; } void *thread_B(void *arg) { printf("IAM IN TREAD B\n"); Buffer *buffer = (Buffer*) arg; sem_wait(&task1); int i=0; int byte_counts[256] = {0}; // 改用for循环,逻辑更清晰,避免size为负时的无限循环 for (i = 0; i < buffer->size; i++) { unsigned char byte = buffer->data[i]; byte_counts[byte]++; } for (int j = 0; j < 256; j++) { printf("Byte-value %02X: %d\n", j, byte_counts[j]); } sem_post(&task2); printf("threadB is done 2\n"); return NULL; }
额外优化说明
- 增加
malloc失败检查,避免空指针问题。 - 调用
sem_destroy销毁信号量,符合资源释放规范。 - 将thread_B中的while循环改为for循环,逻辑更健壮。
内容的提问来源于stack exchange,提问作者1987akam
相关产品推荐
相关产品推荐

