调用sem_wait时程序不等待?POSIX线程信号量问题求助
问题描述
我写了一个C程序,创建了3个线程:1个server线程和2个worker线程。worker线程需分别计算1000行文件里前500个和后500个三位正整数的和,完成后由server线程打印各自的计算结果。但目前信号量似乎无法正常工作,我试过调整线程数、信号量数量及初始值均无效,仅手动调用sleep(5)能得到正确输出,但这违背了项目初衷。
原程序代码:
// simple c program to simulate POSIX thread and semaphore #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <semaphore.h> // define semaphores sem_t s1; FILE *file; int sum1 = 0, sum2 = 0, num1 = 0, num2 = 0; // file name char fileName[10] = "data1.dat"; // server routine void* server_routine() { printf("Server sent signal to worker thread 1\n"); printf("Server sent signal to worker thread 2\n"); sem_wait(&s1); printf("Server recieved completion signal from worker thread 1\n"); sem_wait(&s1); printf("Server recieved completion signal from worker thread 2\n\n"); // print the final results printf("The sum of the first 500 numbers in the file is: %d\n", sum1); printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2); pthread_exit(NULL); } // thread 1 reoutine void* t1_routine() { printf("Thread 1 recieved signal from server\n"); file = fopen(fileName, "r"); for(int i = 0; i < 500; i++) { fscanf(file, "%d", &num1); sum1 += num1; } printf("sum in thread 1: %d\n", sum1); printf("Thread 1 sends completion signal to server\n"); sem_post(&s1); pthread_exit(NULL); } // thread 2 routine void* t2_routine() { printf("Thread 2 recieved signal from server\n"); file = fopen(fileName, "r"); fseek(file, 500 * 5, SEEK_SET); for(int i = 0; i < 500; i++) { fscanf(file, "%d", &num2); sum2 += num2; } printf("sum in thread 2: %d\n", sum2); printf("Thread 2 sends completion signal to server\n"); sem_post(&s1); pthread_exit(NULL); } // main function int main(int argc, char *argv[]) { // define threads pthread_t server, t1, t2; // initialize the semaphore sem_init(&s1, 0, 0); if(pthread_create(&server, NULL, &server_routine, NULL) != 0) { return 1; } if(pthread_create(&t1, NULL, &t1_routine, NULL) != 0) { return 2; } if(pthread_create(&t2, NULL, &t2_routine, NULL) != 0) { return 3; } if(pthread_join(server, NULL) != 0) { return 4; } if(pthread_join(t1, NULL) != 0) { return 5; } if(pthread_join(t2, NULL) != 0) { return 6; } // destroy semaphores sem_close(&s1); // exit thread pthread_exit(NULL); // end return 0; }
问题分析与修复方案
核心问题
- 文件资源竞争:全局
FILE*变量被两个worker线程交替覆盖,导致文件指针混乱,读取数据错误。 - 伪同步逻辑:原程序中server的“发送信号”仅为打印,无实际同步机制,worker线程未等待server指令就直接执行,不符合设计意图。
- 信号量销毁错误:使用
sem_close销毁无名信号量,不符合POSIX标准,应使用sem_destroy。 - 文件定位不可靠:用固定偏移量
500*5定位文件,未考虑实际文件格式(如换行符长度),可能导致读取位置错误。
修复后的代码
// simple c program to simulate POSIX thread and semaphore #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <semaphore.h> // 定义信号量:完成信号量+两个启动信号量 sem_t completion_sem; sem_t start_t1, start_t2; int sum1 = 0, sum2 = 0; const char* fileName = "data1.dat"; // server线程函数 void* server_routine() { printf("Server sent signal to worker thread 1\n"); sem_post(&start_t1); printf("Server sent signal to worker thread 2\n"); sem_post(&start_t2); sem_wait(&completion_sem); printf("Server received completion signal from worker thread 1\n"); sem_wait(&completion_sem); printf("Server received completion signal from worker thread 2\n\n"); // 打印最终结果 printf("The sum of the first 500 numbers in the file is: %d\n", sum1); printf("The sum of the last 500 numbers in the file is: %d\n\n", sum2); pthread_exit(NULL); } // worker线程1函数 void* t1_routine() { sem_wait(&start_t1); printf("Thread 1 received signal from server\n"); FILE* file = fopen(fileName, "r"); if (!file) { perror("Failed to open file in thread 1"); sem_post(&completion_sem); pthread_exit(NULL); } int num; for(int i = 0; i < 500; i++) { if (fscanf(file, "%d", &num) != 1) break; sum1 += num; } fclose(file); printf("sum in thread 1: %d\n", sum1); printf("Thread 1 sends completion signal to server\n"); sem_post(&completion_sem); pthread_exit(NULL); } // worker线程2函数 void* t2_routine() { sem_wait(&start_t2); printf("Thread 2 received signal from server\n"); FILE* file = fopen(fileName, "r"); if (!file) { perror("Failed to open file in thread 2"); sem_post(&completion_sem); pthread_exit(NULL); } int num; // 跳过前500个数字,避免固定偏移的误差 for(int i = 0; i < 500; i++) { if (fscanf(file, "%d", &num) != 1) break; } // 读取后500个数字 for(int i = 0; i < 500; i++) { if (fscanf(file, "%d", &num) != 1) break; sum2 += num; } fclose(file); printf("sum in thread 2: %d\n", sum2); printf("Thread 2 sends completion signal to server\n"); sem_post(&completion_sem); pthread_exit(NULL); } // 主函数 int main(int argc, char *argv[]) { pthread_t server, t1, t2; // 初始化信号量:完成信号量初始0,启动信号量初始0 sem_init(&completion_sem, 0, 0); sem_init(&start_t1, 0, 0); sem_init(&start_t2, 0, 0); if(pthread_create(&server, NULL, server_routine, NULL) != 0) { perror("Failed to create server thread"); return 1; } if(pthread_create(&t1, NULL, t1_routine, NULL) != 0) { perror("Failed to create thread 1"); return 2; } if(pthread_create(&t2, NULL, t2_routine, NULL) != 0) { perror("Failed to create thread 2"); return 3; } if(pthread_join(server, NULL) != 0) { perror("Failed to join server thread"); return 4; } if(pthread_join(t1, NULL) != 0) { perror("Failed to join thread 1"); return 5; } if(pthread_join(t2, NULL) != 0) { perror("Failed to join thread 2"); return 6; } // 销毁无名信号量 sem_destroy(&completion_sem); sem_destroy(&start_t1); sem_destroy(&start_t2); pthread_exit(NULL); return 0; }
关键修复说明
- 文件资源隔离:每个worker线程独立打开、操作、关闭文件,避免全局指针覆盖导致的读取错误。
- 真实同步逻辑:新增两个启动信号量,让worker线程等待server的信号后再执行,完全符合设计流程。
- 可靠文件读取:用循环跳过前500个数字,替代固定偏移量,适配不同文件格式的换行符长度。
- 标准信号量操作:使用
sem_destroy销毁无名信号量,符合POSIX规范。 - 错误处理:增加文件打开和读取的错误判断,提升程序健壮性。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

