perl 如何将分支子程序的散列内容传递回主程序?

xwmevbvl  于 2023-11-22  发布在  Perl
关注(0)|答案(2)|浏览(226)

我需要在主程序中访问通过分叉的子程序生成的哈希的内容。这里具体是我试图做的事情:

use Benchmark;
use File::Find;
use File::Basename;
use File::Path;
use Data::Dumper;
use strict;
use warnings;

print "Process ID: $$ \n";
my @PartitionRoots = qw(/nfs/dir1 /nfs/dir2 /nfs/dir3 /nfs/dir4);
my @PatternsToCheck = qw(prefix1 prefix2);
my @MatchedDirnames = qw();
my $DirCount = 0;
my $Forks = 0;
my @AllDirs = qw();
my %SweepStats = ();

foreach my $RootPath (@PartitionRoots) {
    foreach my $Pattern (@PatternsToCheck) {
        if (grep {-e} glob ("$RootPath/$Pattern*")) {
            my @Files = glob ("$RootPath/$Pattern*");
            foreach my $FileName (@Files) {
                if (-d $FileName) {
                    $DirCount++;
                    push (@AllDirs, $FileName);
                    my $PID = fork;
                    if (not defined $PID) {
                        warn 'Could not fork!\n';
                        next;
                    }
                    if ($PID) {
                        $Forks++;
                        print "In the parent PID ($$), Child pid: $PID Number of forked child processes: $Forks\n";
                    } else {
                        print "In the child PID ($$)\n";
                        find(\&file_stats, $FileName);
                        print "Child ($$) exiting...\n";
                        exit;
                    }
                }
            }
        }
    }
}
for (1 .. $Forks) {
   my $PID = wait();
   print "Parent saw child $PID exit.\n";
}
print "Parent ($$) ending.\n";

print Dumper (\%SweepStats);
foreach my $DirName (@AllDirs) {
    print ("Printing $DirName contents...\n");
    foreach (@{$SweepStats{$DirName}}) {
        my $uname = $_->{uname};
        my $mtime = $_->{mtime};
        my $size = $_->{size};
        my $file = $_->{file};
        print ("$uname $mtime $size $file\n");
    }
}

sub file_stats {
    if (-f $File::Find::name) {
        my $FileName = $_;
        my $PathName = dirname($_);
        my $DirName = basename($_);     
        my $uid = (stat($_))[4];
        my $uname = getpwuid($uid);
        my $size = (stat($_))[7];
        my $mtime = (stat($_))[9];
        if (defined $uname && $uname ne '') {
            push @{$SweepStats{$FileName}}, {path=>$PathName,dir=>$DirName,uname=>$uname,mtime=>$mtime,size=>$size,file=>$File::Find::name};
        } else {
            push @{$SweepStats{$FileName}}, {path=>$PathName,dir=>$DirName,uname=>$uid,mtime=>$mtime,size=>$size,file=>$File::Find::name};
        }
    }
    return;
}

exit;

字符串
.但是Dumper是空的,所以紧接着的解引用和打印也是空的。我知道文件统计信息收集工作正常,因为如果我用print语句替换“push @{$SweepStats{$FileName}}”语句,我就能看到预期的结果。我只需要从全局级别正确访问哈希值,但是我不能完全正确。我在这里做错了什么?有各种各样的帖子都是关于将散列传递给子程序的,但不是相反。

nhn9ugyo

nhn9ugyo1#

fork调用创建了一个新的独立进程。这个子进程和它的父进程不能互相写数据。所以为了在父进程和子进程之间交换数据,我们需要使用一些进程间通信(IPC)机制。
到目前为止,使用一个处理细节的库是最容易的,Parallel::ForkManager似乎在这里非常合适,因为它提供了一种简单的方法来将数据从子进程传递回父进程,并且它有一个简单的队列(将同时进行的进程数量限制在给定的数量)。
下面是一些工作代码,注解如下

use warnings;
use strict;
use feature 'say';

use File::Find;
use File::Spec;
use Parallel::ForkManager;
    
my %file_stats;  # written from callback in run_on_finish()
 
my $pm = Parallel::ForkManager->new(16);

$pm->run_on_finish(
    sub {  # 6th argument is what is passed back from finish()
        my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
        foreach my $file_name (keys %$dataref) {
            $file_stats{$file_name} = $dataref->{$file_name};
        }
    }   
);

my @PartitionRoots  = '.';  # For my tests: current directory,
my @PatternsToCheck = '';   # no filter (pattern is empty string)

my %stats;  # for use by File::Find in child processes

foreach my $RootPath (@PartitionRoots) {
    foreach my $Pattern (@PatternsToCheck) {
        my @dirs = grep { -d } glob "$RootPath/$Pattern*";
        foreach my $dir (@dirs) { 
            #say "Looking inside $dir";       
            $pm->start and next;          # child process
            find(\&get_file_stats, $dir);
            $pm->finish(0, { %stats });   # exits, {%stats} passed back
        }
    }   
}
$pm->wait_all_children;
    
sub get_file_stats {
    return if not -f;
    #say "\t$File::Find::name";

    my ($uid, $size, $mtime) = (stat)[4,7,9];
    my $uname = getpwuid $uid;

    push @{$stats{$File::Find::name}}, {
        path => $File::Find::dir,
        dir  => ( File::Spec->splitdir($File::Find::dir) )[-1],
        uname => (defined $uname and $uname ne '') ? $uname : $uid,
        mtime => $mtime,
        size => $size,
        file => $File::Find::name
    };
}

字符串
评论

  • 所有这一切的主要问题是:在您的三级层次结构的哪一部分产生子进程?我把它留在问题中,每个目录的子进程在哪里分叉。如果(大多数)目录有许多文件,这可能是合适的;但如果不是这样,每个子进程几乎没有工作要做,那么它可能会变得太忙碌,开销可能会降低/否认加速
  • %stats hash是File::Find存储它找到的数据所必需的,需要在所有循环之外声明,以便在sub中看到它。因此,它被所有子进程继承,但每个子进程都有自己的副本,我们不必担心数据重叠等问题
  • 我简化(和纠正)代码以外的分叉以及,以下似乎对我来说是理想的。请让我知道,如果是关闭
  • 请参阅链接文档,例如this post及其链接以了解详细信息

有许多库可以用来显示复杂的数据结构,我使用Data::Dump,目的是简单地打印出来

use Data::Dump qw(dd pp);
...
dd \%file_stats;  # or: say "Stats for all files: ", pp \%file_stats;


其紧凑的输出,而最广泛使用的是核心Data::Dumper

use Data::Dumper
...
say Dumper \%file_stats;


它也“理解”数据结构(所以你可以大多eval他们回来)。
(Note:在这种情况下,可能会有 * 很多 * 输出!所以重定向到一个文件,或者在第一次迭代后退出这些循环,看看它是如何进行的。
†当一个进程被分叉时,父进程的变量和数据都可以被子进程使用。出于效率的考虑,它们不会立即被复制,所以最初子进程确实会读取父进程的数据。但是父进程或子进程中的fork调用之后生成的任何数据都不能被其他进程看到。

bgtovc5b

bgtovc5b2#

试试这个模块:IPC::Shareable。它是由perldoc perlipc推荐的,你可以在这里找到你问题的答案。

相关问题