【问题标题】:Perl: Run recursive jobs in parallelPerl:并行运行递归作业
【发布时间】:2014-06-03 15:59:53
【问题描述】:

我有一个递归函数,它调用系统命令来列出文件和目录。对于每个目录,它都会再次调用自己。

此过程可能需要一段时间。这就是我想运行并行作业的原因。

我正在研究 ForkManager,但它不允许创建新的子分支。由于子进程的数量应限制为 10,我在考虑“工人”的概念。有 10 个工人在等待作业执行。

我的递归函数:

sub pullDataFromDbWithDirectory {
    my $_dir = $_[0];
    my @list = ();

    if ($itemCount lt $maxNumberOfItems) {
        my @retval = grep { /dir|file/ } map { s/^Dir\s+|^File\s+|\n//g; $_ } qx($omnidb -filesystem $filesystem  '$label'  -listdir '$_dir');

        foreach my $item (@retval) {
            $itemCount++;

            push(@list,$item) if $item =~ /^file/;

            if ($item =~ /^dir/) {
                my $subdir = "$_dir/$item";
                $data{$subdir} = ();

                if ($recursive) {
                    pullDataFromDbWithDirectory($subdir);
                }
            }
        }

        $data{$_dir} = \@list;
    }
}

任何帮助将不胜感激。

更新:

问题解决了。感谢您的输入。我修改了我的代码:

sub pullDataFromDbWithDirectory {
    my $_dir = $_[0];

    if ($itemCount <= $maxNumberOfItems) {
        my @retval = grep { /dir|file/ } map { s/^Dir\s+|^File\s+|\n//g; $_ } qx($omnidb -filesystem $filesystem  '$label'  -listdir '$_dir');

        foreach my $item (@retval) {
            $itemCount++;
            my $file = "$_dir/$item";
            push(@data,$file);

            if ($item =~ /^dir/) {
                $worker->enqueue($file);
                print "Add $file to queue\n" if $debug;
            }
        }
    }
}

sub doOperation () {
    my $ithread = threads->tid();
    while (my $folder = $worker->dequeue()) {
        print "Read $folder from queue\n" if $debug;
        pullDataFromDbWithDirectory($folder);
    }
}

my @threads = map threads->create(\&doOperation), 1 .. $maxNumberOfParallelJobs;
pullDataFromDbWithDirectory($directory);
$worker->enqueue((undef) x $maxNumberOfParallelJobs);
$_->join for @threads;

【问题讨论】:

  • 您需要为此进行进程间通信。支持 IPC 的一个模块是 Child:metacpan.org/pod/Child
  • 递归地发现目标目录,然后并行处理它们是否可以接受?如果您不是特别需要子叉,不妨使用最简单的方法。
  • 问题是我必须运行omnidb 命令来列出文件和文件夹。而且它不支持递归列表。 Foreach 子文件夹我必须运行omnidb 命令,这需要相当长的时间。我想并行运行它们。也许将目录填充到正在并行处理的队列中..
  • 看看GNU parallel

标签: perl recursion parallel-processing


【解决方案1】:

我会重写您的代码以使用适当的 Perl 模块,例如 File::Find,这样会更有效。

use File::Find;
my %data;
find(\&wanted, @directories_to_search);
sub wanted {
  $data{$File::Find::dir} = $_;

}

对于并行操作,我会像这样使用 Thread::Queue:

use strict;
use warnings;
use threads;

use threads;
use Thread::Queue;

my $q = Thread::Queue->new();    # A new empty queue
my %seen: shared;

# Worker thread
my @thrs = threads->create(\&doOperation ) for 1..5;#for 5 threads
add_file_to_q('/tmp/');
$q->enqueue('//_DONE_//') for @thrs;
$_->join() for @thrs;

sub add_file_to_q {
  my $dir = shift;
  my @files = `ls -1 $dir/`;chomp(@files);
  #add files to queue
  foreach my $f (@files){
    # Send work to the thread
    $q->enqueue($f);
    print "Pending items: "$q->pending()."\n";
  }
}



sub doOperation () {
    my $ithread = threads->tid() ;
    while (my $filename = $q->dequeue()) {
      # Do work on $item
      sleep(1) if ! defined $filename;
      return 1 if $filename eq '//_DONE_//';
      next if $seen{$filename};
      print "[id=$ithread]\t$filename\n";
      $seen{$filename} = 1;
      ### add files if it is a directory (check with symlinks, no file with //_DONE_// name!)
      add_file_to_q($filename) if -d $filename;
    }
    return 1;
}

【讨论】:

  • 我不确定 File::Find 是否更有效,因为我没有解析任何文件系统。我正在为 HP Data Protector 运行一些omnidb 命令,该命令返回文件和目录列表。但它不支持我尝试实现的递归列表。对于每个子目录,我必须使用指定的目录重新运行 omnidb 命令。感谢线程:队列示例。我去看看
  • 感谢您的解决方案。现在可以了。我在上面添加了我的工作代码。
猜你喜欢
  • 2013-10-02
  • 1970-01-01
  • 2014-01-25
  • 2023-03-29
  • 2019-12-11
  • 1970-01-01
  • 2018-10-20
  • 2021-03-20
  • 1970-01-01
相关资源
最近更新 更多