为什么Parallel::ForkManager可以创建子进程,但不能并行处理它们?



在处理一些遗留代码时,我被以下问题难住了。

我们有一个程序可以自动将配置推送到多个路由器。执行此操作的Perl程序使用Parallel::ForkManager库来完成这些推送的并行化。

在程序的开头出现了以下语句…

$p = new Parallel::ForkManager($multi); 

创建分叉的主循环如下…

foreach my $node (@files) {
    # only if we're running in parallel
    if (ref($p)) {
         $p->start() and next;
    }
    # ship the file & path off to the main sub that does
    # all the heavy lifting
    do_push($node);
    # only if we're running in parallel
    if (ref($p)) {
        $p->finish();
    }
}
# only if we're running in parallel
if (ref($p)) {
    $p->wait_all_children();
}

do_push($node)子例程太长,无法在这里详细说明,但简而言之,它为节点建立一个日志文件,然后通过ssh连接到路由器并推送配置。然后退出并完成日志记录。

从表面上看,一切似乎都在工作,进程分叉,结果如预期的那样。

然而…

当单独的进程被创建时,路由器的连接和配置的推送并不是并行发生的。很容易通过netstat -an验证ssh连接只是按顺序发生,而不是并行发生。只有在一个连接关闭后,下一个路由器才会连接。简而言之,我一次只能获得一个ssh连接。

有没有人知道为什么会这样,或者在哪里继续解决这个问题?

[编辑]

根据注释,这里是子程序中发生的事情的摘要。出于篇幅和安全的考虑,我不能全部放在这里。

子程序的开头如下…

sub do_push {
    my($data) = @_;
    my($path,$router) = @$data;
    $|++;  # hopefully speed things up
    ($DEBUG or ($v > 2)) && print STDERR "PROCESSING: $router ($path)n";

接下来的过程是…

  • 打开一个日志文件,写入文件。
  • 实例化连接路由器的模块
  • 连接路由器
  • 推送命令,验证命令,如果成功则提交。
  • 记录所有的时间到日志文件。

子程序的结尾如下…

    # finished with this router...
    # append our archive file
    my $ts = tv_interval($t0, [gettimeofday()]);
    (!$DEBUG && $archive) && print S "ROUTER: $router:$ts:$dispositionn";
    ($DEBUG or ($v > 2)) && print STDERR "ROUTER COMPLETE: $router -> $ts secn";
    # unlock and close our files
    (($DEBUG > 1) or ($v > 2)) && print STDERR "UNLOCK AND CLOSE: $archfilen";
    (($DEBUG > 1) or ($v > 2)) && print STDERR "UNLOCK AND CLOSE: $summaryn";
    (!$DEBUG && $archive) && flock(F, LOCK_UN);
    (!$DEBUG && $archive) && flock(S, LOCK_UN);
    (!$DEBUG && $archive) && close(F);
    (!$DEBUG && $archive) && close(S);
    return;
}

如果在调试模式下运行,子程序中的第一条调试语句:"PROCESSING: $router ($path)n"只在前一个路由器完全完成子程序后打印。肯定有多个进程分叉,我已经验证了。只是"某些东西"阻止了进程运行,直到前一个进程完成。我在努力寻找那个"东西"是什么。

[solved]

我讨厌回答我自己的问题,但是我确实找到了阻塞的原因…

原来是文件访问问题。在do_push()循环中,程序记录到两个文件。一个文件是用来记录结果的文件本身,这个文件是子进程独有的,没有问题。然而,该程序也写入了一个"摘要"文件。所有子进程都记录到这个文件。

将会发生的是,第一个线程将打开文件,记录一些东西,连接到路由器并推出命令。一旦完成,它将关闭文件。当然,其他线程会阻塞,等待访问"摘要"文件。

对于小型路由器推送,这一切都发生得如此之快,以至于看起来这一切都是并行发生的。只有当我们不得不向每个路由器推出1000条线路时,才发现这并不是并行进行的。

相关内容

最新更新