多进程管道
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' 将保持不变。
目前正在做一些功课,遇到了困难。目标是生成 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' 将保持不变。