同时在多个文件上运行Perl脚本

时间:2015-03-19 19:58:26

标签: multithreading perl

我编写了一个Perl脚本,它逐行读入数据文件,进行一些计算并返回3个文件作为输出;我也写了它,以便它读取我目录中的每个* .csv文件,当时一个文件,为每个输入文件返回3个单独的输出文件(因此对于10个csv输入文件,当我的脚本是完了,我有30个输出文件。)

然而,当我运行我的脚本时,我发现它只在一个核心上运行。我想做的是让我的脚本同时在几个输入文件上运行:这甚至可能吗?或者,或者,什么是更好的选择?我正在使用Windows机器。

1 个答案:

答案 0 :(得分:0)

在Perl中使用更多处理器有两个(主要)选项。

线程和叉子。它们之间存在一定的相似性,但存在一些重要的差异。 fork()是Unix上的本机系统调用,它非常有效(它经常使用)。在Windows上你没有它 - perl虽然模仿它的功能很好。

fork完全克隆你的程序 - 它使'父'和'子'和唯一区别是fork的返回码。代码从完全相同的点恢复,因此您可以以一些稍微奇怪的行为结束。

例如,当您运行时注意:

#!/usr/bin/perl
use strict;
use warnings;

my $pid = fork();

if ( $pid ) { 
   print "$$ is the parent - child is $pid\n";
}
else {
   print "$$ is the child\n";
}

你应该知道 - 之前存在的每个变量仍然在每个'fork'中定义,但它是一个单独的副本。这将引导您进入下一个挑战,即进程间通信。这是一个足够大的主题,它有自己的perl文档页面perlipc

虽然谈到更多的平行,但是fork可能有点尴尬,因为它是一个低级别的调用。你认为这打印多少行?

#!/usr/bin/perl
use strict;
use warnings;

my @fruits = qw ( apple pear lemon lime cucumber ); 

foreach my $fruit ( @fruits ) {
    my $pid = fork();
    if ( $pid ) { 
        print "Parent $$ with a child of $pid has a fruit of $fruit\n";
    } 
    else {
       print "Child of $$ has a fruit of $fruit\n";
    }
}

因为fork是嵌套的,所以它的发生次数比您直观猜测的要多。使用循环对fork过多也很容易,并且您可以创建拒绝服务条件。

幸运的是,有一个解决方案 - Parallel::Forkmanager实现了一些控制分支的简单机制,这使得它更加顺畅。

#!/usr/bin/perl
use strict;
use warnings;

use Parallel::ForkManager; 

my @fruits = qw ( apple pear lemon lime cucumber ); 
my $manager = Parallel::ForkManager -> new ( 5 ); 

print "Parent: $$\n";
foreach my $fruit ( @fruits ) {
    $manager -> start and next;
    print "Child of $$ - $fruit\n";
    $manager -> finish; 
}

$manager -> wait_all_children;

为了完整起见 - 我还要提到threads。它们是另一种做事方式,但它们与其他语言一样,在轻微的直觉上并不轻量级。他们也是“气馁”的地位:

  

Perl提供的“基于解释器的线程”并不是人们可能期望或希望的快速,轻量级的多任务处理系统。线程的实现方式使其易于滥用。很少有人知道如何正确使用它们或能够提供帮助。   官方不鼓励在perl中使用基于解释器的线程。

因此,哪里的叉子很容易有很多很多,线程基本上最好被认为是单独的进程。

#!/usr/bin/perl
use strict;
use warnings;
use threads;

sub thread_sub {
   print threads -> self -> tid(). ": @_\n";
}

my @fruits = qw ( apple pear lemon lime cucumber ); 

foreach my $fruit ( @fruits ) {
    threads -> create ( \&thread_sub, $fruit );
}

foreach my $thr ( threads -> list ) {
    $thr -> join;
}

在任何一种情况下,您应该知道 - 并行处理意味着您的代码不再以明显的顺序方式发生。这意味着如果你不小心,你会有一些真正的果味和时髦的错误,这些错误很难调试。因此,在尝试接近并行性之前,请确保您的代码首先按顺序工作。

您还应该知道 - 只要您的限制因素纯粹是CPU,您就只能获得线性性能改进。一般不是。磁盘IO总是慢得多。你提到处理几个文件。如果重点是处理,而不是读取数据 - 那么并行性将有所帮助。

但是磁盘非常慢,并且通过尝试从多个位置流式传输数据来“颠倒”它们会使速度更慢。因此,通过平行IO密集型任务(磁盘遍历,批量文件读取等),您不会获得太多 - 如果有的话 - 并且您可以很容易地使事情变得更糟。