多子进程访问同一vector求和异常问题排查
问题描述
我想创建一个包含10000个初始值为5的vector,然后创建10个子进程,每个子进程对vector的一个分段执行运算并返回该分段的元素总和。我用一个大小为10的sums数组存储各分段总和,但父进程最终计算所有分段总和时结果为0,可每个子进程打印的对应索引总和是正确的。
代码实现
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #define VECTOR_SIZE 10000 #define INITIAL_VALUE 5 #define SECTION_SIZE 1000 int do_operations(int vetor[], int section_index); int sum_all(int sums[]); int main() { // Initializing the vector int vetor[VECTOR_SIZE]; for (int i = 0; i < VECTOR_SIZE; i++) { vetor[i] = INITIAL_VALUE; } // Doing the operations int sums[VECTOR_SIZE/SECTION_SIZE]; int status; for(int n = 0; n < 10; n++){ if (fork() != 0) { /* Parent */ waitpid( -1, &status, 0); } else { /* Child */ sums[n] = do_operations(vetor, n); printf("Sum of Section %d: %d\n", n, sums[n]); exit(0); } } printf("Sum of all sections: %d\n", sum_all(sums)); return 0; }
当前输出
Sum of Section 0: 10000 Sum of Section 1: 10000 Sum of Section 2: 10000 Sum of Section 3: 10000 Sum of Section 4: 10000 Sum of Section 5: 10000 Sum of Section 6: 10000 Sum of Section 7: 10000 Sum of Section 8: 10000 Sum of Section 9: 10000 Sum of all sections: 0
预期输出
Sum of Section 0: 10000 Sum of Section 1: 10000 Sum of Section 2: 10000 Sum of Section 3: 10000 Sum of Section 4: 10000 Sum of Section 5: 10000 Sum of Section 6: 10000 Sum of Section 7: 10000 Sum of Section 8: 10000 Sum of Section 9: 10000 Sum of all sections: 100000
问题原因
fork()创建子进程时,系统会为子进程复制一份父进程的内存空间,父子进程的内存是完全独立的。你在子进程中修改的sums数组是子进程自己的副本,父进程的sums数组根本没有被修改——栈上未初始化的变量值是随机的,这里恰好显示为0,导致最终总和计算错误。
解决方案
要让子进程的计算结果能被父进程获取,需要使用进程间通信(IPC)机制,以下是两种常用实现方式:
方法一:使用管道
每个子进程通过管道将计算结果传递给父进程,父进程读取后存入自己的sums数组。
修改后的完整代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #define VECTOR_SIZE 10000 #define INITIAL_VALUE 5 #define SECTION_SIZE 1000 int do_operations(int vetor[], int section_index); int sum_all(int sums[]); int main() { int vetor[VECTOR_SIZE]; for (int i = 0; i < VECTOR_SIZE; i++) { vetor[i] = INITIAL_VALUE; } int sums[10]; int pipes[10][2]; // 每个子进程对应一个管道 // 提前创建所有管道 for (int n = 0; n < 10; n++) { if (pipe(pipes[n]) == -1) { perror("pipe failed"); exit(EXIT_FAILURE); } } for(int n = 0; n < 10; n++){ pid_t pid = fork(); if (pid > 0) { /* Parent */ close(pipes[n][1]); // 父进程不需要写管道,关闭写端 } else if (pid == 0) { /* Child */ close(pipes[n][0]); // 子进程不需要读管道,关闭读端 int section_sum = do_operations(vetor, n); printf("Sum of Section %d: %d\n", n, section_sum); // 将结果写入管道 write(pipes[n][1], §ion_sum, sizeof(section_sum)); close(pipes[n][1]); exit(EXIT_SUCCESS); } else { perror("fork failed"); exit(EXIT_FAILURE); } } // 父进程读取所有管道的结果 for (int n = 0; n < 10; n++) { read(pipes[n][0], &sums[n], sizeof(sums[n])); close(pipes[n][0]); } // 等待所有子进程退出 while (wait(NULL) != -1); printf("Sum of all sections: %d\n", sum_all(sums)); return 0; } // 补全do_operations函数(根据你的运算逻辑实现,这里示例为元素乘2后求和) int do_operations(int vetor[], int section_index) { int start = section_index * SECTION_SIZE; int end = start + SECTION_SIZE; int sum = 0; for (int i = start; i < end; i++) { sum += vetor[i] * 2; } return sum; } // 补全sum_all函数 int sum_all(int sums[]) { int total = 0; for (int i = 0; i < 10; i++) { total += sums[i]; } return total; }
方法二:使用共享内存
通过系统调用创建共享内存区域,让父子进程共享sums数组空间,子进程修改的内容父进程可以直接访问。
示例代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <sys/ipc.h> #include <sys/shm.h> #define VECTOR_SIZE 10000 #define INITIAL_VALUE 5 #define SECTION_SIZE 1000 int do_operations(int vetor[], int section_index); int sum_all(int sums[]); int main() { int vetor[VECTOR_SIZE]; for (int i = 0; i < VECTOR_SIZE; i++) { vetor[i] = INITIAL_VALUE; } // 创建共享内存 key_t key = ftok("shm_key", 1); // 生成唯一键值 int shmid = shmget(key, sizeof(int)*10, IPC_CREAT | 0666); if (shmid == -1) { perror("shmget failed"); exit(EXIT_FAILURE); } int *sums = (int*)shmat(shmid, NULL, 0); // 关联共享内存到进程地址空间 if (sums == (int*)-1) { perror("shmat failed"); exit(EXIT_FAILURE); } int status; for(int n = 0; n < 10; n++){ pid_t pid = fork(); if (pid > 0) { /* Parent */ waitpid(-1, &status, 0); } else if (pid == 0) { /* Child */ sums[n] = do_operations(vetor, n); printf("Sum of Section %d: %d\n", n, sums[n]); shmdt(sums); // 子进程脱离共享内存 exit(EXIT_SUCCESS); } else { perror("fork failed"); exit(EXIT_FAILURE); } } printf("Sum of all sections: %d\n", sum_all(sums)); // 释放共享内存 shmdt(sums); shmctl(shmid, IPC_RMID, NULL); return 0; } // 补全do_operations和sum_all函数(同方法一) int do_operations(int vetor[], int section_index) { int start = section_index * SECTION_SIZE; int end = start + SECTION_SIZE; int sum = 0; for (int i = start; i < end; i++) { sum += vetor[i] * 2; } return sum; } int sum_all(int sums[]) { int total = 0; for (int i = 0; i < 10; i++) { total += sums[i]; } return total; }
内容的提问来源于stack exchange,提问作者Lucas Toscano
相关产品推荐
相关产品推荐

