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

多子进程访问同一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], &section_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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:07:04