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

调用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;
}
问题分析与修复方案

核心问题

  1. 文件资源竞争:全局FILE*变量被两个worker线程交替覆盖,导致文件指针混乱,读取数据错误。
  2. 伪同步逻辑:原程序中server的“发送信号”仅为打印,无实际同步机制,worker线程未等待server指令就直接执行,不符合设计意图。
  3. 信号量销毁错误:使用sem_close销毁无名信号量,不符合POSIX标准,应使用sem_destroy。
  4. 文件定位不可靠:用固定偏移量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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 23:50:30