仅用pthread互斥锁实现三线程同步的问题求助
问题描述
需要实现三个线程:每个线程打开不同输入文件,每次读取一个字符到对应全局变量,按thread1→thread2→thread3的循环顺序执行;主线程按该顺序将全局变量字符写入hw5.out。现有代码运行后实际输出重复最后一组字符,而非预期的按序循环输出。要求仅使用pthread mutex同步,不能用标志、全局计数、信号量或条件变量,寻求解决建议。
输入文件每行一个字符且行数相同,预期输出按thread1、thread2、thread3的字符循环写入,实际输出重复最后一组字符。
现有代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <string.h> // 全局字符变量 char globalVAR1; char globalVAR2; char globalVAR3; // 定义互斥锁 pthread_mutex_t mutexOne; pthread_mutex_t mutexTwo; pthread_mutex_t mutexThree; void *threadOne(void *arg){ FILE *fileOne; fileOne = fopen("hw5-1.in", "r"); char mChr1; // 读取每个字符 if(fileOne == NULL){ perror("file one error"); } while(!feof(fileOne)){ fscanf(fileOne, "%c\n", &mChr1); printf("char read: %c\n", mChr1); pthread_mutex_lock(&mutexOne); globalVAR1 = mChr1; pthread_mutex_unlock(&mutexOne); } fclose(fileOne); return NULL; } // 线程二 void *threadTwo(void *arg){ FILE *fileTwo; fileTwo = fopen("hw5-2.in", "r"); char mChr2; // 读取每个字符 if(fileTwo == NULL){ perror("file two error"); } while(!feof(fileTwo)){ fscanf(fileTwo, "%c\n", &mChr2); printf("char read: %c\n", mChr2); pthread_mutex_lock(&mutexTwo); globalVAR2 = mChr2; pthread_mutex_unlock(&mutexTwo); } fclose(fileTwo); return NULL; } // 线程三 void *threadThree(void *arg){ FILE *fileThree; fileThree = fopen("hw5-3.in", "r"); char mChr3; // 读取每个字符 if(fileThree == NULL){ perror("file three error"); } while(!feof(fileThree)){ fscanf(fileThree, "%c\n", &mChr3); printf("char read: %c\n", mChr3); pthread_mutex_lock(&mutexThree); globalVAR3 = mChr3; pthread_mutex_unlock(&mutexThree); } fclose(fileThree); return NULL; } // 主线程负责打开输出文件并写入 int main(){ // 打开输出文件 FILE *mainWriting; mainWriting = fopen("hw5.out", "w"); if(mainWriting == NULL){ perror("open output file error"); exit(1); } // 统计输入文件行数 FILE *count; count = fopen("hw5-1.in", "r"); if(count == NULL){ perror("count file error"); exit(1); } char cc; int nu=0; while(fscanf(count, "%c\n", &cc) == 1){ nu += 1; } fclose(count); // 初始化互斥锁 pthread_mutex_init(&mutexOne, NULL); pthread_mutex_init(&mutexTwo, NULL); pthread_mutex_init(&mutexThree, NULL); pthread_t pthreadOne, pthreadTwo, pthreadThree; pthread_create(&pthreadOne, NULL, threadOne, NULL); pthread_create(&pthreadTwo, NULL, threadTwo, NULL); pthread_create(&pthreadThree, NULL, threadThree, NULL); // 循环写入输出文件 while(nu != 0){ pthread_mutex_lock(&mutexOne); fputc(globalVAR1, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexOne); sleep(1); pthread_mutex_lock(&mutexTwo); fputc(globalVAR2, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexTwo); sleep(1); pthread_mutex_lock(&mutexThree); fputc(globalVAR3, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexThree); nu -=1; } pthread_join(pthreadOne, NULL); pthread_join(pthreadTwo, NULL); pthread_join(pthreadThree, NULL); fclose(mainWriting); pthread_mutex_destroy(&mutexOne); pthread_mutex_destroy(&mutexTwo); pthread_mutex_destroy(&mutexThree); return 0; }
问题根源
- 线程无顺序控制:三个线程会快速读完整个文件,全局变量最终只保留每个文件的最后一个字符,主线程循环读取的都是这组最终值,导致输出重复。
- 互斥锁仅保护变量读写:当前的互斥锁只保证全局变量的读写原子性,但没有约束线程的执行顺序,线程不会等待主线程取走当前字符就覆盖全局变量。
feof使用错误:while(!feof(file))会导致最后一次读取失败后仍进入循环,可能写入无效字符。
解决方案(仅用pthread mutex)
利用互斥锁的"令牌传递"机制,让主线程控制每个线程的执行时机,确保thread1→thread2→thread3的循环顺序:
修改步骤
1. 调整互斥锁初始状态
主线程初始化互斥锁后,提前锁住mutexTwo和mutexThree,只允许thread1先执行:
// 初始化互斥锁 pthread_mutex_init(&mutexOne, NULL); pthread_mutex_init(&mutexTwo, NULL); pthread_mutex_init(&mutexThree, NULL); // 初始锁住thread2和thread3的锁,只放行thread1 pthread_mutex_lock(&mutexTwo); pthread_mutex_lock(&mutexThree);
2. 重写线程逻辑
每个线程不再一次性读完文件,而是每次读取一个字符后,等待主线程处理完再读取下一个。利用互斥锁的阻塞特性实现等待:
void *threadOne(void *arg){ FILE *fileOne; fileOne = fopen("hw5-1.in", "r"); if(fileOne == NULL){ perror("file one error"); return NULL; } char mChr1; // 已知行数为nu,直接循环nu次,避免feof问题 int *total = (int*)arg; for(int i=0; i<*total; i++){ // 读取字符(用fgetc更可靠,过滤换行) while((mChr1 = fgetc(fileOne)) != EOF && mChr1 == '\n'); if(mChr1 == EOF) break; // 写入全局变量,通知主线程可以读取 pthread_mutex_lock(&mutexOne); globalVAR1 = mChr1; pthread_mutex_unlock(&mutexOne); // 等待主线程处理完当前字符,再读取下一个 pthread_mutex_lock(&mutexOne); pthread_mutex_unlock(&mutexOne); } fclose(fileOne); return NULL; }
threadTwo和threadThree逻辑完全一致,只需替换对应的互斥锁和全局变量:
threadTwo使用mutexTwo和globalVAR2threadThree使用mutexThree和globalVAR3
3. 修改主线程循环逻辑
主线程每次循环按顺序读取三个线程的字符,读取完成后解锁下一个线程的互斥锁,传递执行令牌:
// 传递总行数给线程 pthread_create(&pthreadOne, NULL, threadOne, &nu); pthread_create(&pthreadTwo, NULL, threadTwo, &nu); pthread_create(&pthreadThree, NULL, threadThree, &nu); // 循环写入输出文件 while(nu-- > 0){ // 读取thread1的字符,完成后解锁thread2的锁 pthread_mutex_lock(&mutexOne); fputc(globalVAR1, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexTwo); // 读取thread2的字符,完成后解锁thread3的锁 pthread_mutex_lock(&mutexTwo); fputc(globalVAR2, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexThree); // 读取thread3的字符,完成后解锁thread1的锁,开启下一轮 pthread_mutex_lock(&mutexThree); fputc(globalVAR3, mainWriting); fputc('\n', mainWriting); pthread_mutex_unlock(&mutexOne); }
4. 修复feof问题
用已知的总行数nu控制线程的循环次数,避免feof的错误判断;读取字符时用fgetc过滤换行符,确保只读取有效字符。
最终效果
修改后,线程会严格按照thread1→thread2→thread3的顺序依次读取单个字符,主线程读取完成后才允许下一轮读取,全局变量不会被提前覆盖,输出文件会按预期顺序写入每组字符。
内容的提问来源于stack exchange,提问作者brocoli
相关产品推荐
相关产品推荐

