Multithreading 如何在Perl中开始多线程

Multithreading 如何在Perl中开始多线程,multithreading,perl,Multithreading,Perl,我有一个perl程序,运行时间超过13个小时。我认为引入多线程可能会使它受益,但我以前从未这样做过,我不知道如何开始 以下是我的情况: 我有一个包含数百个文本文件的目录。我使用基本for循环遍历目录中的每个文件,并进行一些处理(对文件本身进行文本处理,对文件调用外部程序,并对其进行压缩)。完成后,我将转到下一个文件。我继续这样做,以串行方式一个接一个地处理每个文件。这些文件彼此完全独立,并且进程不返回任何值(成功/失败代码除外),因此这似乎是多线程的一个很好的候选者 我的问题是: 如何重写基本循

我有一个perl程序,运行时间超过13个小时。我认为引入多线程可能会使它受益,但我以前从未这样做过,我不知道如何开始

以下是我的情况: 我有一个包含数百个文本文件的目录。我使用基本for循环遍历目录中的每个文件,并进行一些处理(对文件本身进行文本处理,对文件调用外部程序,并对其进行压缩)。完成后,我将转到下一个文件。我继续这样做,以串行方式一个接一个地处理每个文件。这些文件彼此完全独立,并且进程不返回任何值(成功/失败代码除外),因此这似乎是多线程的一个很好的候选者

我的问题是:

  • 如何重写基本循环以利用线程?这里似乎有几种穿线方式
  • 如何控制当前正在运行的线程数?如果有N个可用的内核,如何将线程数限制为N或N-N
  • 我是否需要手动管理线程计数,或者Perl会为我这样做

  • 如果您有任何建议,我们将不胜感激。

    因为您的线程只是要启动一个进程并等待它结束,所以最好绕过中间商,只使用进程。除非您使用的是Windows系统,否则我建议您在场景中使用Parallel::ForkManager

    use Parallel::ForkManager qw( );
    
    use constant MAX_PROCESSES => ...;
    
    my $pm = Parallel::ForkManager->new(MAX_PROCESSES);
    
    my @qfns = ...;
    
    for my $qfn (@qfns) {
       my $pid = $pm->start and next;
       exec("extprog", $qfn)
          or die $!;
    }
    
    $pm->wait_all_children();
    
    如果希望避免在Windows中使用不必要的中间线程,则必须使用类似于以下内容的内容:

    use constant MAX_PROCESSES => ...;
    
    my @qfns = ...;
    
    my %children;
    for my $qfn (@qfns) {
       while (keys(%children) >= MAX_PROCESSES) {
          my $pid = wait();
          delete $children{$pid};
       }
    
       my $pid = system(1, "extprog", $qfn);
       ++$children{$pid};
    }
    
    while (keys(%children)) {
       my $pid = wait();
       delete $children{$pid};
    }
    

    由于您的线程只是要启动一个进程并等待它结束,所以最好绕过中间商,只使用进程。除非您使用的是Windows系统,否则我建议您在场景中使用Parallel::ForkManager

    use Parallel::ForkManager qw( );
    
    use constant MAX_PROCESSES => ...;
    
    my $pm = Parallel::ForkManager->new(MAX_PROCESSES);
    
    my @qfns = ...;
    
    for my $qfn (@qfns) {
       my $pid = $pm->start and next;
       exec("extprog", $qfn)
          or die $!;
    }
    
    $pm->wait_all_children();
    
    如果希望避免在Windows中使用不必要的中间线程,则必须使用类似于以下内容的内容:

    use constant MAX_PROCESSES => ...;
    
    my @qfns = ...;
    
    my %children;
    for my $qfn (@qfns) {
       while (keys(%children) >= MAX_PROCESSES) {
          my $pid = wait();
          delete $children{$pid};
       }
    
       my $pid = system(1, "extprog", $qfn);
       ++$children{$pid};
    }
    
    while (keys(%children)) {
       my $pid = wait();
       delete $children{$pid};
    }
    

    有人给了你一个很好的例子。fork不是Windows上的原生版本,所以我倾向于使用线程

    为了完整性,这里有一个线程工作原理的大致概念(IMO是更好的方法之一,而不是重新启动线程)

    #/usr/bin/perl
    严格使用;
    使用警告;
    使用线程;
    使用线程::队列;
    我的$nthreads=5;
    我的$process_q=Thread::Queue->new();
    我的$failed_q=Thread::Queue->new();
    #这是一个子例程,但它“作为线程”运行。
    #当它启动时,它继承程序状态“原样”。例如。
    #以上所有变量声明都适用,但对
    #程序中的值为“线程本地”,除非
    #变量定义为“共享”。
    #幕后-Thread::Queue是“共享”数组。
    下属{
    #注意-这将无限期地循环,直到您关闭队列。
    #使用$process_q->end
    #一旦我们把所有我们想处理的事情都排好队,我们就会这样做
    #潜艇完成并整齐地退出。
    #然而如果你不结束它,这将永远等待。
    while(my$server=$process\u q->dequeue()){
    chomp($server);
    打印线程->self()->tid()。“:pinging$server\n”;
    my$result=`/bin/ping-c1$server`;
    如果($?){$failed\u q->enqueue($server)}
    打印$result;
    }
    }
    #将任务插入线程队列。
    
    打开(我的$input_fh,“有人给了你一个分叉的例子。分叉不是Windows上固有的,所以我倾向于使用线程

    为了完整性,这里有一个线程工作原理的大致概念(IMO是更好的方法之一,而不是重新启动线程)

    !/usr/bin/perl
    严格使用;
    使用警告;
    使用线程;
    使用线程::队列;
    我的$nthreads=5;
    我的$process_q=Thread::Queue->new();
    我的$failed_q=Thread::Queue->new();
    #这是一个子例程,但它“作为线程”运行。
    #当它启动时,它继承程序状态“原样”。
    #以上所有变量声明都适用,但对
    #程序中的值为“线程本地”,除非
    #变量定义为“共享”。
    #幕后-Thread::Queue是“共享”数组。
    下属{
    #注意-这将无限期地循环,直到您关闭队列。
    #使用$process_q->end
    #一旦我们把所有我们想处理的事情都排好队,我们就会这样做
    #潜艇完成并整齐地退出。
    #然而如果你不结束它,这将永远等待。
    while(my$server=$process\u q->dequeue()){
    chomp($server);
    打印线程->self()->tid()。“:pinging$server\n”;
    my$result=`/bin/ping-c1$server`;
    如果($?){$failed\u q->enqueue($server)}
    打印$result;
    }
    }
    #将任务插入线程队列。
    
    打开(my$input_fh),“抓取文件列表,然后使用并行::ForkManager循环,在该循环中使用
    exec
    启动处理器,则多线程处理不会加快程序的速度。它实际上可能会减慢程序的速度!@AKHolland,文件压缩通常是CPU的任务bound@ikegami这要视情况而定,在着手重写他的程序之前,当然值得做一些评测。@Akhland,profiling?你是说基准测试。由于缓存的原因,很难准确地进行评测,但是following会给出一个想法:
    time bash-c'extprog file1;extprog file2'
    vs
    time bash-c'extprog file1&extprog file2'
    抓取文件列表,然后使用并行::ForkManager循环,在该循环中使用
    exec
    启动处理器。如果您的程序是IO绑定的(听起来可能是),则多线程处理不会加快程序的速度。它实际上可能会减慢程序的速度!@AKHolland,文件压缩通常是CPU的任务bound@ikegami这要视情况而定,在着手重写他的程序之前,当然值得做一些评测。@Akhland,profiling?你是说基准测试。由于缓存的原因,很难准确地进行评测,但是following会给出一个想法:
    timebash-c'extprog file1;extprog file2'
    vs
    timeba