【问题标题】:ForkManager (Perl) quits the parent job before the children jobs are readyForkManager (Perl) 在子作业准备好之前退出父作业
【发布时间】:2019-03-08 02:52:21
【问题描述】:

我有以下 perl 代码:

use Parallel::ForkManager;
  my $maxpro = 15;
  my $pm = Parallel::ForkManager->new($maxpro);
my $SampId;
my $SampDir;
my $RunId;
my $Runfile;
my %hash;
 foreach $RunId(keys(%hash)) {
 $pm->start and next;
 $SampId = $hash{$RunId};
 $Runfile = $rundir . $RunId . "fastq";

 if ($SampId =~ m/16S/) {
     $SampDir = $SiXSSdir . $SampId . "_Split/"; 
 }
 elsif ($SampId =~ m/ITS/) {
     $SampDir = $ITSdir . $SampId . "_Split/";
 }
 #make a directory for each file
 my $dirouteach = $outdir . $SampId . "Single_directory.out";
 my $dirm4 = "bsub -o $dirouteach mkdir $SampDir";

 system('bash', '-c', "$dirm4") == 0 or die "Can't create each single subdirectories for either 16S or ITS, or both, see line 114" . "\n";
$pm ->finish;
}

$pm -> wait_all_children();

但是,当我这样运行时,所有子项目都在运行,但主脚本过早退出,甚至在子项目完成之前,它也没有给出任何错误消息。我还尝试运行它删除$pm ->finish;,但在这种情况下,脚本被卡住并永远等待,直到孩子们完成。 我确定我在使用 fork manager(我以前从未使用过)时做错了。我的目标只是用它来并行化我的工作,然后等到所有工作都完成,然后在我的脚本中继续前进。 任何帮助将不胜感激,谢谢!

【问题讨论】:

  • 乍一看你的用法对我来说是正确的。确保您使用的是严格和警告,最好在您需要它们的范围内声明您的变量,而不是在文件顶部。例如foreach my $RunId ...my $SampId = ... 我假设您在开始循环之前以某种方式填充了 %hash
  • 是的,他们之前填充了哈希,我没有在这里添加它,因为它并不真正相关。正如我所写,foreach 循环运行顺利,只是 forkmanager 在孩子仍在运行时退出了父级,你们知道为什么吗?
  • 大胆猜测:您的一个子进程可能导致了 SIGPIPE。尝试在循环之前在父级中设置$SIG{PIPE} = 'IGNORE';
  • (1) 为什么要使用排队系统——有必要吗?这可能会对任何事情产生影响,当然也会影响到这一点。 (2) 您是否确认子代码按顺序正确运行(甚至仅针对一种情况)?您可以通过注释掉几行来做到这一点。
  • 删除$pm->finish; 是完全错误的。 $pm->finish;exit; 相同。如果你删除它,孩子将开始执行父母的代码并声明创建自己的孩子。

标签: perl parallel-processing fork


【解决方案1】:

bsub 已经是并行计算框架的一部分。 bsub 命令本身运行很快;它只是将作业提交给作业调度程序并退出。它不应该等待它安排的作业完成,因此您的所有子进程都应该快速完成,并且主程序也将快速退出。

如果你想自己控制并行处理,你应该在没有bsub的情况下执行每个命令。如果您使用的是bsub,那么您不需要自己进行分叉或使用像Parallel::ForkManager 这样的后台进程管理器。

【讨论】:

    【解决方案2】:

    我没有调试 Parallel::ForkManager 的想法,但这里有一个使用 IO::Async 的示例,它更灵活一些,同时类似地为原始 fork 和 waitpid 提供了一个很好的包装器。

    use strict;
    use warnings;
    use IO::Async::Loop;
    use Future;
    
    my $loop = IO::Async::Loop->new;
    my @futures;
    my %hash;
    ... # populate %hash
    foreach my $RunId (keys %hash) {
      my $dirm4;
      ... # build $dirm4
      my $future = $loop->new_future;
      my $process = $loop->open_process(
        command => ['bash', '-c', $dirm4],
        on_finish => sub { $future->done(@_) },
        on_exception => sub { $future->fail(@_) },
      );
      push @futures, $future;
    }
    
    # run event loop until all the futures are done, or throw an exception if one fails to start the command
    my @exit_codes = Future->needs_all(@futures)->get;
    

    如果不想一次启动,也可以使用Future::Utils实现队列,我最近在this answer写了一个例子。

    【讨论】:

    • 谢谢大家,感谢您的提示,我学到了很多,实际上这不是forkmanager的问题,正如ikegami所说,没关系,我按照@zdim提示运行循环forkmanager ,它变成了一个变量问题(我没有提供访问它们的完整路径,这就是问题所在)。无论如何感谢大家的宝贵提示
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-24
    相关资源
    最近更新 更多