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

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

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

  1. use Benchmark;
  2. use File::Find;
  3. use File::Basename;
  4. use File::Path;
  5. use Data::Dumper;
  6. use strict;
  7. use warnings;
  8. print "Process ID: $$ \n";
  9. my @PartitionRoots = qw(/nfs/dir1 /nfs/dir2 /nfs/dir3 /nfs/dir4);
  10. my @PatternsToCheck = qw(prefix1 prefix2);
  11. my @MatchedDirnames = qw();
  12. my $DirCount = 0;
  13. my $Forks = 0;
  14. my @AllDirs = qw();
  15. my %SweepStats = ();
  16. foreach my $RootPath (@PartitionRoots) {
  17. foreach my $Pattern (@PatternsToCheck) {
  18. if (grep {-e} glob ("$RootPath/$Pattern*")) {
  19. my @Files = glob ("$RootPath/$Pattern*");
  20. foreach my $FileName (@Files) {
  21. if (-d $FileName) {
  22. $DirCount++;
  23. push (@AllDirs, $FileName);
  24. my $PID = fork;
  25. if (not defined $PID) {
  26. warn 'Could not fork!\n';
  27. next;
  28. }
  29. if ($PID) {
  30. $Forks++;
  31. print "In the parent PID ($$), Child pid: $PID Number of forked child processes: $Forks\n";
  32. } else {
  33. print "In the child PID ($$)\n";
  34. find(\&file_stats, $FileName);
  35. print "Child ($$) exiting...\n";
  36. exit;
  37. }
  38. }
  39. }
  40. }
  41. }
  42. }
  43. for (1 .. $Forks) {
  44. my $PID = wait();
  45. print "Parent saw child $PID exit.\n";
  46. }
  47. print "Parent ($$) ending.\n";
  48. print Dumper (\%SweepStats);
  49. foreach my $DirName (@AllDirs) {
  50. print ("Printing $DirName contents...\n");
  51. foreach (@{$SweepStats{$DirName}}) {
  52. my $uname = $_->{uname};
  53. my $mtime = $_->{mtime};
  54. my $size = $_->{size};
  55. my $file = $_->{file};
  56. print ("$uname $mtime $size $file\n");
  57. }
  58. }
  59. sub file_stats {
  60. if (-f $File::Find::name) {
  61. my $FileName = $_;
  62. my $PathName = dirname($_);
  63. my $DirName = basename($_);
  64. my $uid = (stat($_))[4];
  65. my $uname = getpwuid($uid);
  66. my $size = (stat($_))[7];
  67. my $mtime = (stat($_))[9];
  68. if (defined $uname && $uname ne '') {
  69. push @{$SweepStats{$FileName}}, {path=>$PathName,dir=>$DirName,uname=>$uname,mtime=>$mtime,size=>$size,file=>$File::Find::name};
  70. } else {
  71. push @{$SweepStats{$FileName}}, {path=>$PathName,dir=>$DirName,uname=>$uid,mtime=>$mtime,size=>$size,file=>$File::Find::name};
  72. }
  73. }
  74. return;
  75. }
  76. exit;

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

nhn9ugyo

nhn9ugyo1#

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

  1. use warnings;
  2. use strict;
  3. use feature 'say';
  4. use File::Find;
  5. use File::Spec;
  6. use Parallel::ForkManager;
  7. my %file_stats; # written from callback in run_on_finish()
  8. my $pm = Parallel::ForkManager->new(16);
  9. $pm->run_on_finish(
  10. sub { # 6th argument is what is passed back from finish()
  11. my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
  12. foreach my $file_name (keys %$dataref) {
  13. $file_stats{$file_name} = $dataref->{$file_name};
  14. }
  15. }
  16. );
  17. my @PartitionRoots = '.'; # For my tests: current directory,
  18. my @PatternsToCheck = ''; # no filter (pattern is empty string)
  19. my %stats; # for use by File::Find in child processes
  20. foreach my $RootPath (@PartitionRoots) {
  21. foreach my $Pattern (@PatternsToCheck) {
  22. my @dirs = grep { -d } glob "$RootPath/$Pattern*";
  23. foreach my $dir (@dirs) {
  24. #say "Looking inside $dir";
  25. $pm->start and next; # child process
  26. find(\&get_file_stats, $dir);
  27. $pm->finish(0, { %stats }); # exits, {%stats} passed back
  28. }
  29. }
  30. }
  31. $pm->wait_all_children;
  32. sub get_file_stats {
  33. return if not -f;
  34. #say "\t$File::Find::name";
  35. my ($uid, $size, $mtime) = (stat)[4,7,9];
  36. my $uname = getpwuid $uid;
  37. push @{$stats{$File::Find::name}}, {
  38. path => $File::Find::dir,
  39. dir => ( File::Spec->splitdir($File::Find::dir) )[-1],
  40. uname => (defined $uname and $uname ne '') ? $uname : $uid,
  41. mtime => $mtime,
  42. size => $size,
  43. file => $File::Find::name
  44. };
  45. }

字符串
评论

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

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

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


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

  1. use Data::Dumper
  2. ...
  3. say Dumper \%file_stats;


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

展开查看全部
bgtovc5b

bgtovc5b2#

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

相关问题