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

C语言多进程同步问题:如何使用信号量实现共享内存子进程同步

问题根因

你当前代码子进程串行执行的核心原因是:在创建子进程的for循环内,每次fork出子进程后立刻调用了wait(NULL),父进程会阻塞等待当前子进程执行完毕退出后,才会进入下一轮循环创建下一个子进程,自然不会有并行效果。

另外你每个子进程仅操作自己索引对应的共享内存结构体,不存在多进程同时写同一块内存的冲突,如果你只需要子进程并行执行,不需要严格同时启动,甚至不需要信号量,仅调整wait的位置即可;如果需要所有子进程严格同时启动执行累加逻辑,才需要用信号量实现同步启动屏障。

修改后完整代码(包含信号量同步启动)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>
// 新增信号量头文件
#include <sys/sem.h>

#define NUMBER_OF_CARS 10
#define MIN 25 // 随机数生成最小值
#define MAX 40 // 随机数生成最大值

typedef struct {
    unsigned int totalTime;
} car;

car *shared_memory;
int sem_id; // 信号量id

// 封装信号量P操作(减1,阻塞等待信号)
void sem_p() {
    struct sembuf op = {0, -1, 0};
    semop(sem_id, &op, 1);
}

// 封装信号量V操作(加n,唤醒等待的进程)
void sem_v(int n) {
    struct sembuf op = {0, n, 0};
    semop(sem_id, &op, 1);
}

int drive(int i);
unsigned int generateNumber(void);
void display();

int main(void)
{
/***************************************************
*           创建共享内存        *
****************************************************/
    int segment_id = shmget(IPC_PRIVATE, sizeof(car) * NUMBER_OF_CARS, 0666 | IPC_CREAT);
    if (segment_id == -1) {
        perror("shmget() 执行失败!");
        exit(EXIT_FAILURE);
    }

    shared_memory = shmat(segment_id, NULL, 0);
    if (shared_memory == (void *) (-1)) {
        perror("shmat() 执行失败!");
        exit(EXIT_FAILURE);
    }

    // 初始化共享内存的totalTime为0,避免初始值随机导致结果异常
    memset(shared_memory, 0, sizeof(car) * NUMBER_OF_CARS);

    /***************************************************
    *           创建同步启动信号量        *
    ****************************************************/
    sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
    if (sem_id == -1) {
        perror("semget() 执行失败!");
        exit(EXIT_FAILURE);
    }
    // 信号量初始值设为0,所有子进程启动后先等待启动信号
    semctl(sem_id, 0, SETVAL, 0);

    /**********************************************************
     *               创建子进程              *
     **********************************************************/
    for (int i = 0; i < NUMBER_OF_CARS; ++i)
    {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork 执行失败!");
            exit(EXIT_FAILURE);
        }

        if(pid == 0) {
            // 子进程先等待启动信号
            sem_p();
            drive(i);
            exit(EXIT_SUCCESS);
        }
    }

    // 所有子进程创建完成,发送启动信号:一次性加10,所有等待的子进程都能通过P操作
    sem_v(NUMBER_OF_CARS);

    // 等待所有子进程执行完毕
    for(int i=0; i<NUMBER_OF_CARS; i++) {
        wait(NULL);
    }

    // 所有子进程执行完后统一展示最终结果
    display();

    /********  资源清理  *********/
    shmdt(shared_memory);
    shmctl(segment_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID); // 删除信号量,避免系统资源泄漏

    exit(EXIT_SUCCESS);
}

unsigned int timeMaxi = 5400;

int drive( int i ) {
    srand(time(NULL) + getpid());

    unsigned int number;

    while ( shared_memory[i].totalTime <= timeMaxi )
    {
        number = generateNumber();
        shared_memory[i].totalTime += number;
    }
    return 0;
}

void display(void) {
    for (int i = 0; i < NUMBER_OF_CARS; i++){
        printf("Total %d : %d\n", i, shared_memory[i].totalTime);
    }
}

unsigned int generateNumber(void)
{
    return rand()%(MAX-MIN+1)+MIN;
}

核心修改说明

  • 新增System V信号量逻辑实现子进程同步启动:信号量初始值为0,所有子进程创建完成后父进程一次性将信号量值加10,所有等待的子进程会同时被唤醒开始执行累加逻辑
  • 把原来放在循环内的wait、display逻辑移到了所有子进程创建完成之后,父进程等待所有10个子进程都执行完毕后才统一展示最终结果
  • 新增共享内存初始化逻辑,避免共享内存初始值随机导致结果异常
  • 新增信号量资源清理逻辑,避免系统资源泄漏

内容的提问来源于stack exchange,提问作者bendour

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 17:18:00