You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅用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;
}

问题根源
  1. 线程无顺序控制:三个线程会快速读完整个文件,全局变量最终只保留每个文件的最后一个字符,主线程循环读取的都是这组最终值,导致输出重复。
  2. 互斥锁仅保护变量读写:当前的互斥锁只保证全局变量的读写原子性,但没有约束线程的执行顺序,线程不会等待主线程取走当前字符就覆盖全局变量。
  3. 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和globalVAR2
  • threadThree使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 15:50:01