多进程管道

Pipe for multiple processes

目前正在做一些功课,遇到了困难。目标是生成 100,000 个数字并通过将工作分成 10 个过程(每个过程 10,000 个数字)将它们全部加在一起

我想我已经弄清楚了如何分叉进程(希望如此),但是使用 Pipe() 来中继每个子进程的小计不起作用...下面的程序 returns 44901 每个子进程和 449010 为 运行 总数。

我很挣扎,但我觉得这很简单,我应该能够理解。

main()
{
    int i;
    pid_t pid;
    int status = 0;
    int fd[2];
    int runningTotal = 0;
    pipe(fd);

    int t;
    int r;

    for (i = 0; i < 10; i++) {
        pid = fork();
        if (pid == 0){
            close(fd[0]);
            t = ChildProcess();
            write(fd[1], &t, sizeof(t));
            exit(0);
        }
        close(fd[1]);
        read(fd[0], &r, sizeof(r));
        runningTotal = runningTotal + r;
        wait(&status);
    }

    printf("%i\n", runningTotal);
}

int ChildProcess() {
    int i;
    int total = 0;
    int r = 0;

    for (i = 0; i < 10000; i++) {
        r = rand() % 10; // 0 to 10
        total = total + r;
    }

    printf("%i\n", total);
    return total;
}

初步诊断

如果您担心 children 都产生相同的值,那么问题是它们都使用相同的随机序列,因为您没有在任何地方调用 srand() .您需要将其命名为 once per child,每个 child.

使用不同的种子

它不是 100% 可靠,但您可能会在每个 child 中使用 srand(time(0) + getpid()); — 甚至只是 getpid(),因为这些值肯定是不同的。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int ChildProcess(void)
{
    int total = 0;
    srand(time(0) + getpid());

    for (int i = 0; i < 10000; i++)
    {
        int r = rand() % 10; // 0 to 9 (not 10).
        total = total + r;
    }

    printf("%i\n", total);
    return total;
}

进一步审查

其实仔细一看,还有一个问题。 parent 进程在分叉第一个 child 后关闭管道的写入端,因此后续的 children 没有可用的文件描述符可供使用。读取的值将始终是第一个 child 中的值。所以,你需要做更认真的工作。

int main(void)
{
    int fd[2];
    pipe(fd);  // Missing error check

    for (int i = 0; i < 10; i++) {
        pid_t pid = fork();
        if (pid == 0){
            close(fd[0]);
            int t = ChildProcess();
            write(fd[1], &t, sizeof(t));  // Missing error check?
            exit(0);
        }
        // Print PID here?  Error check?
    }

    close(fd[1]);

    int r;
    int runningTotal = 0;
    while (read(fd[0], &r, sizeof(r)) > 0)  // Debugging opportunities here
        runningTotal = runningTotal + r;

    while (wait(0) > 0)  // Lots of debugging opportunities here
        ;

    printf("%i\n", runningTotal);
    return 0;
}

通常情况下,每个子进程都会使用一个单独的管道,否则父进程不可能知道它读取的数据来自哪个进程。不过,我认为在这种特殊情况下这不是什么大问题,因为在这里,您实际上并不关心。虽然它仍然让我有点畏缩,但我认为你确实可以只用一根管道来完成这个特定的任务。

事实上,我认为您的问题根本不是管道的问题。它与rand()。所有子进程都计算完全相同的(伪)随机数序列,因为它们都使用相同的(默认)种子。如果你想产生不同的数字序列,那么你需要在每个子进程中调用 srand() ,在每个子进程中给出不同的种子。 rand() 将生成的数字序列完全由其开始的种子决定。

另请注意,如果系统的随机数生成器有任何好处,那么各个进程计算的所有总和应该彼此非常接近,并且与您报告的结果非常接近。这是统计学中中心极限定理的结果,但您可以将其简单地认为是较大的结果平均平衡较小的结果。计算余数 mod 10.

可能会产生轻微偏差

鉴于此代码:(已发布代码的摘录)

for (i = 0; i < 10; i++) {
    pid = fork();
    if (pid == 0){
        close(fd[0]);
        t = ChildProcess();
        write(fd[1], &t, sizeof(t));
        exit(0);
    }
    close(fd[1]);
    read(fd[0], &r, sizeof(r));
    runningTotal = runningTotal + r;
    wait(&status);
}

顺序有问题。

当父级在循环的第一次迭代期间关闭 fd[1] 时,该文件描述符不会 'magically' 在循环的下一次迭代中再次打开。

循环中的父级代码需要检查调用 read() 的返回值以确保操作成功。 (它可能在循环的第一次迭代后不成功,因此变量 'r' 将保持不变。