OS课程作业:5个分离线程互斥同步异常求助
问题描述
这是操作系统课程的学术练习,要求仅使用mutex锁与解锁同步5个分离线程,禁止强制线程串行执行,线程启动后除mutex外不受外部影响,父线程不能调用pthread_join。
当前代码创建5个分离线程,用于读取文件并更新全局变量,但仅3个线程输出ID,且没有线程进入while循环;若在main的线程创建循环中取消sleep(1)的注释可得到预期输出,但这属于强制串行,不符合要求。
输出结果
thread: 6156515168 thread: 6156515192 thread: 6156515176
原代码
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <pthread.h> pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER; // declaring mutex int FileNameHelper=1; int balance=0; void* detatchedThread(void *param){ long tid = (long)param; char* base = "data"; char filename[256]; char buf[100]; sprintf(filename, "%s%d.in", base, FileNameHelper); //creates data1.in, data2.in... FileNameHelper ++; FILE *inputFile = fopen(filename, "r"); printf ("thread: %ld\n", tid); // critical sec line if(fgets(buf, sizeof buf, inputFile) == NULL) return NULL; // could not read first line sleep(1); // make sure each thread runs long enough to get the random update behavior required. pthread_mutex_lock(&mutex_lock); //we are in the critical section, lock mutex while(fgets(buf, sizeof buf, inputFile) != NULL) { int val; if(sscanf(buf, "%d", &val) != 1){ break; } printf("%d\n", val); balance += val; printf ("Account balance after thread %ld is $%d\n", tid, balance); } pthread_mutex_unlock(&mutex_lock); if(buf[0] != 'W') return NULL;// last line data was invalid pthread_exit(NULL); } int main(){ pthread_t th[5]; //initialize the mutex if(pthread_mutex_init(&mutex_lock, NULL) != 0){ printf("\nmutex init has failed\n"); return 1; } //call the 5 threads, Detach the threads once they are created. for (int i = 0; i < 5; i++){ pthread_create(&th[i], NULL, detatchedThread, (void *)&th[i]); pthread_detach(th[i]); //sleep(1); uncommenting this line gives me the expected behavior } pthread_mutex_destroy(&mutex_lock); return 0; }
问题分析与解决建议
核心问题点
- 主线程提前退出并销毁mutex:main函数创建完线程后立即销毁mutex并返回,此时大部分线程还未执行完成,进程直接终止导致未执行的线程被强制结束;同时mutex被销毁后,线程调用
pthread_mutex_lock会触发未定义行为。 - 共享变量
FileNameHelper无同步保护:多个线程同时读写该全局变量,会出现竞态条件,导致多个线程获取到相同的文件编号,打开同一个文件甚至打开失败,后续fgets直接返回NULL使线程提前退出,无法进入while循环。 - 线程参数传递错误:创建线程时传递
&th[i],但循环中i的值会持续变化,线程启动后可能读取到错误的tid值,甚至是非法内存地址。 - 未检查文件打开结果:
fopen可能失败(比如文件不存在),后续直接使用inputFile调用fgets会导致崩溃或未定义行为。
修复方案
- 主线程等待所有线程完成:新增全局计数器
completed_threads,每个线程结束前加锁更新计数器;主线程循环加锁检查计数器是否达到5,直到所有线程完成再销毁mutex并退出。 - 同步
FileNameHelper的访问:修改FileNameHelper时必须加mutex锁,避免竞态条件。 - 修复线程参数传递:直接传递
(void *)(long)i作为线程ID(因为int和long在多数平台下可以安全转换),避免指针引用循环变量的问题。 - 增加文件打开错误检查:判断
fopen的返回值,若失败则直接退出线程。 - 调整sleep位置(可选):将
sleep(1)移到mutex锁外,不影响同步逻辑的同时保留随机执行的效果。
修改后的代码示例
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <pthread.h> pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER; int FileNameHelper = 1; int balance = 0; int completed_threads = 0; // 新增:记录已完成的线程数 void* detatchedThread(void *param){ long tid = (long)param; char base[] = "data"; char filename[256]; char buf[100]; FILE *inputFile = NULL; // 同步获取文件编号 pthread_mutex_lock(&mutex_lock); int file_num = FileNameHelper++; pthread_mutex_unlock(&mutex_lock); sprintf(filename, "%s%d.in", base, file_num); inputFile = fopen(filename, "r"); // 检查文件是否打开成功 if (!inputFile) { printf("thread %ld: failed to open file %s\n", tid, filename); goto thread_cleanup; } printf("thread: %ld\n", tid); // 读取第一行,检查是否成功 if(fgets(buf, sizeof(buf), inputFile) == NULL) { printf("thread %ld: failed to read first line of %s\n", tid, filename); goto thread_cleanup; } sleep(1); // 保留随机执行效果 // 进入临界区更新balance pthread_mutex_lock(&mutex_lock); while(fgets(buf, sizeof(buf), inputFile) != NULL) { int val; if(sscanf(buf, "%d", &val) != 1){ break; } printf("%d\n", val); balance += val; printf("Account balance after thread %ld is $%d\n", tid, balance); } pthread_mutex_unlock(&mutex_lock); // 检查最后一行是否有效 if(buf[0] != 'W') { printf("thread %ld: last line invalid\n", tid); } thread_cleanup: if (inputFile) { fclose(inputFile); } // 更新完成线程数 pthread_mutex_lock(&mutex_lock); completed_threads++; pthread_mutex_unlock(&mutex_lock); pthread_exit(NULL); } int main(){ pthread_t th[5]; if(pthread_mutex_init(&mutex_lock, NULL) != 0){ printf("mutex init has failed\n"); return 1; } for (int i = 0; i < 5; i++){ // 传递i的副本作为线程ID if(pthread_create(&th[i], NULL, detatchedThread, (void *)(long)i) != 0) { printf("failed to create thread %d\n", i); return 1; } pthread_detach(th[i]); } // 等待所有线程完成 while(1) { pthread_mutex_lock(&mutex_lock); if (completed_threads >= 5) { pthread_mutex_unlock(&mutex_lock); break; } pthread_mutex_unlock(&mutex_lock); usleep(100000); // 避免空循环占用CPU } pthread_mutex_destroy(&mutex_lock); return 0; }
内容的提问来源于stack exchange,提问作者Mathew Cherian
相关产品推荐
相关产品推荐

