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
相关产品推荐
相关产品推荐

