在Perl中分叉多个子节点并使用管道进行双向通信

时间:2011-12-07 21:48:41

标签: perl fork pipe

我正在尝试创建一个具有多处理功能的小型Perl程序。由于我的要求中存在一些小的变化,我无法在任何地方找到任何类似的示例脚本。

我需要从STDIN读取一个大的日志文件,并将第一个N号码(一个大号码)再次给第一个子进程,然后将第二个N行给第二个子进程等。我还有一个常量定义这是允许并发运行的最大子进程数。一旦达到最大子女数,父母将等待孩子完成其工作,并给予另外N行。

父进程还会收集每个子进程在完成时返回的多行(5-10行)输出并将其存储在数组中。然后,Parent继续处理此数组内容并最终显示结果。

是否有更好的示例脚本我可以修改和使用,或者有人可以通过在这里共享来帮助我吗?我更喜欢只使用管道进行流程互通,并尽可能简化事务。

编辑: 有人能举例说明如何只使用IO :: Handle模块中的管道来实现这个目标吗?

2 个答案:

答案 0 :(得分:5)

使用Forks::Super,可以轻松限制同时进程的数量并处理进程间通信。例如,

use Forks::Super MAX_PROC => 10,     # allow 10 simultaneous processes
                 ON_BUSY => 'queue'; # don't block when >=10 jobs are active

@loglines = <>;

# set up all the background jobs
while (@loglines > 0) {
    $pid = fork {
        args => [ splice @loglines, 0, $N ],  # to pass to sub, below
        child_fh => "out",    # make child STDOUT readable by parent
        sub => sub {
            my @loglines = @_;
            my @result = ... do something with loglines ...
            print @results;   # use $pid->read_stdout() to read in child
        }
    };
}

# get the results
while ($pid = waitpid -1, 0) {
    last if $pid == -1;
    my @results_from_job = $pid->read_stdout();
    push @results, @results_from_job;
}

答案 1 :(得分:-1)

我发现线程对于这种过程来说要简单得多。您需要线程和Threads :: Queue模块。该过程是设置一个队列来提供工作线程,一个队列让他们返回结果。工作线程只是一个读取记录,处理记录并将结果发回的函数。我只是将这些代码放在一起,并没有对它进行测试,因此它可能是错误的,但我认为显示了一般的想法:

use threads ();
use Thread::Queue;
#
#
#            Set limit on number of workers
#
my $MAX_THREADS = 5;
my $EOD = "\n\n";
#
#
#            Need a queue to feed the workers
#            and one for them to return results
#
my $Qresult  = Thread::Queue->new();
my $rec;
my $n;
#
#
#            load STDIN into the input queue
#
my $Qin = Thread::Queue->new(<>);
#
#
#            start worker threads
#
for($n = 0; $n < $MAX_THREADS; ++$n)
{
    async{ProcessRecord($n);};
    $Qin->enqueue($EOD);            #    need terminator for each
}
#
#
#
#            Wait for the results to come in
#
$n = 0;
while($n < $MAX_THREADS)
{
            $rec = $q->dequeue();
            if($rec eq $EOD)
            {
                ++$n;
                next;
            }

            :
            :
            :
    #-- process result --#
            :
            :
            :


    threads->yield();    #    let other threads get a chance
    sleep 1;
}
exit;    
######################################    
#
#
#            Worker threads draw from the queue
#            when a "terminator" is read, quit;
#
sub ProcessRecord 
{
    my $rec;
    my $result;

    while(1)
    {
        $rec = $Qin->dequeue();
        last if $rec eq $EOD;
            :
            :
            :
        #-- process record --#
            :
            :
            :
        $Qresult->enqueue($result);

        threads->yield();    #    let other threads get a chance
    }

    threads->exit();    
}