多个分叉和 IO:Pipe

Multiple forks and IO:Pipe

我正在尝试构建一个程序,该程序会创建一些分叉并将分叉的结果写回主程序。因此我尝试使用 IO::Pipe

sub ForkRequests {

  my $pipe = IO::Pipe->new();
  my $pid;

  foreach my $feature ( @features ) {

    if ( $pid = fork() ) {
      $pipe->reader();
      while ( <$pipe> ) {
        print $_. "\n";
      }
    }
    elsif ( defined $pid ) {

      #child
      $pipe->writer();

      #somecalculations [...]
      print $pipe $calcresults;
    }
  }
}

我从模块的文档中获得了用于创建管道的代码。

如果我现在尝试执行,我会收到一条错误消息

Can't locate object method "reader" via package "IO::Pipe::End" at lmtest3.pl line 56.
Can't locate object method "writer" via package "IO::Pipe::End" at lmtest3.pl line 63.
Can't locate object method "reader" via package "IO::Pipe::End" at lmtest3.pl line 56, <GEN0> line 1.
Can't locate object method "writer" via package "IO::Pipe::End" at lmtest3.pl line 63, <GEN0> line 1.

所以,我的代码似乎并没有启动一个管道对象,而是一个IO::Pipe::End。 所以我的问题是,任何人都可以看到那里的错误吗?为什么它 return 是错误的对象,如何正确完成?

编辑

我对一些服务器有一些请求(大多数时候 1 个请求到 7 个服务器)。 这些请求名称保存在@features中,将在#somecalculations.

点执行

因为服务器响应很慢,我希望这些请求并行启动。他们都必须返回主程序并将回复打印到控制台。

我试过这个代码

sub ForkRequests {

  my $i = 0;
  my @pipes;
  my $pid;

  foreach my $feature ( @features ) {

    @pipes[$i] = IO::Pipe->new();
    if ( $pid = fork() ) {
      @pipes[$i]->reader();
    }
    elsif ( defined $pid ) {

      #child
      @pipes[$i]->writer();

      # calculations
      my $w = @pipes[$i];
      print $w $calc;
      print $w "end\n";
    }

    $i++;
  }
}

if ( $pid == 1 ) {
  while ( 1 ) {
    foreach my $pipe ( @pipes ) {
      while ( <$pipe> ) {
        unless ( $_ == "end" ) {
          print $_. "\n";
        }
        else { last; }
      }
    }
  }
}
else {
  exit;
}

}

如上所述,为了保存这些管道,但我在读取它们时仍然遇到问题,因为程序在得到答案之前就退出了。

问题是您正在分叉多个子进程,但试图对所有子进程使用相同的管道。

reader 方法将 $pipe 转换为可以从中读取数据的 IO::Pipe::End 对象,因此第一个子节点已正确连接。但是您随后在同一个 $pipe 上再次调用 reader 并抛出错误,因为它不再是正确的 class.

的对象

您只需为每个子进程创建一个新管道:

sub fork_requests {

    for my $feature ( @features ) {

        my $pipe = IO::Pipe->new;
        my $pid;

        if ( $pid = fork ) {
            $pipe->reader;
            print while <$pipe>;
        }
        elsif ( defined $pid ) {
            $pipe->writer;
            # some calculations . . .
            print $pipe $calcresults;
            exit;
        }
    }

}

更新

好的,我想我明白你需要什么了。这个完整的程序应该告诉你。

我已经编写了 fork_requests 以便它需要一个功能列表作为参数,并且我已经编写了子代码以便它休眠两秒钟以模拟处理时间然后简单地打印名称功能。

正如我建议的那样,父代码将所有管道存储在一个数组中,并按照它们排队的顺序打印每个管道的输出。所有五个子进程在两秒后完成,因此父进程在这段时间内暂停,然后打印最初传入的功能。

use strict;
use warnings;

use IO::Pipe;

STDOUT->autoflush;

fork_requests('A' .. 'E');

sub fork_requests {

    my @pipes;

    for my $feature ( @_ ) {

        my $pipe = IO::Pipe->new;
        my $pid;

        if ( $pid = fork ) {
            $pipe->reader;
            push @pipes, $pipe;
        }
        elsif ( defined $pid ) {
            $pipe->writer;
            select $pipe;
            # some calculations . . .
            sleep 2;
            my $calcresults = $feature;
            print $calcresults, "\n";
            exit;
        }
    }

    for my $pipe ( @pipes ) {
      print while <$pipe>;
    }

}

输出

A
B
C
D
E