如何在Perl中使用线程?

时间:2011-10-21 13:59:08

标签: multithreading perl

我想在Perl中使用线程来提高程序的速度...例如我想在这段代码中使用20个线程:

use IO::Socket;
my $in_file2 = 'rang.txt';
open DAT,$in_file2;
my @ip=<DAT>;
close DAT;
chomp(@ip);
foreach my $ip(@ip)
{
    $host = IO::Socket::INET->new(
        PeerAddr => $ip,
        PeerPort => 80,
        proto    => 'tcp',
        Timeout=> 1
    ) 
    and open(OUT, ">>port.txt");
    print OUT $ip."\n";
    close(OUT);
}

在上面的代码中,我们给出了一个ips列表并扫描给定的端口。我想在这段代码中使用线程。有没有其他方法可以提高我的代码速度?

感谢。

3 个答案:

答案 0 :(得分:6)

您可能希望查看AnyEvent::SocketCoro::SocketPOEParallel::ForkManager,而不是使用主题。

答案 1 :(得分:4)

答案 2 :(得分:3)

Perl可以同时进行线程化和分叉。 &#34;螺纹&#34;官方不推荐 - 在很大程度上是因为它没有被很好地理解,并且 - 或许有点违反规范 - 不像线程在某些编程语言中那样轻量级。

如果你特别热衷于线程,那么“工作人员”会在线程模型比每个任务产生一个线程要好得多。你可能会用某些语言来做后者 - 在perl中效率非常低。

因此你可能会这样做:

#!/usr/bin/env perl

use strict;
use warnings;

use threads;
use Thread::Queue;
use IO::Socket;

my $nthreads = 20;

my $in_file2 = 'rang.txt';

my $work_q   = Thread::Queue->new;
my $result_q = Thread::Queue->new;

sub ip_checker {
    while ( my $ip = $work_q->dequeue ) {
        chomp($ip);
        $host = IO::Socket::INET->new(
            PeerAddr => $ip,
            PeerPort => 80,
            proto    => 'tcp',
            Timeout  => 1
        );
        if ( defined $host ) {
            $result_q->enqueue($ip);
        }
    }
}

sub file_writer {
    open( my $output_fh, ">>", "port.txt" ) or die $!;
    while ( my $ip = $result_q->dequeue ) {
        print {$output_fh} "$ip\n";
    }
    close($output_fh);
}


for ( 1 .. $nthreads ) {
    push( @workers, threads->create( \&ip_checker ) );
}
my $writer = threads->create( \&file_writer );

open( my $dat, "<", $in_file2 ) or die $!;
$work_q->enqueue(<$dat>);
close($dat);
$work_q->end;

foreach my $thr (@workers) {
    $thr->join();
}

$result_q->end;
$writer->join();

这使用一个队列来为一组(20)工作线程提供一个IP列表,并通过它们进行处理,通过writer线程进行整理和打印结果。

但由于线程不再被推荐,更好的方法是使用Parallel::ForkManager,你的代码可能有点像这样:

#!/usr/bin/env perl

use strict;
use warnings;

use Fcntl qw ( :flock );
use IO::Socket;

my $in_file2 = 'rang.txt';
open( my $input,  "<", $in_file2 )  or die $!;
open( my $output, ">", "port.txt" ) or die $!;

my $manager = Parallel::ForkManager->new(20);
foreach my $ip (<$input>) {
    $manager->start and next;

    chomp($ip);
    my $host = IO::Socket::INET->new(
        PeerAddr => $ip,
        PeerPort => 80,
        proto    => 'tcp',
        Timeout  => 1
    );
    if ( defined $host ) {
        flock( $output, LOCK_EX );    #exclusive or write lock
        print {$output} $ip, "\n";
        flock( $output, LOCK_UN );    #unlock
    }
    $manager->finish;
}
$manager->wait_all_children;
close($output);
close($input);

多处理时需要特别注意文件IO,因为整点都不再是你的执行顺序。因此,在另一个线程已经打开但没有刷新到磁盘的情况下,最终会有不同的线程破坏文件。

我注意到你的代码 - 你似乎依赖于打开文件失败,以便不打印它。这不是一件好事,特别是当您的文件句柄没有词法范围时。

但是在我上面概述的两种多处理范例中(还有其他一些,这些是最常见的)你还需要处理文件IO序列化。请注意您的结果&#39;两者都是随机顺序,因为它非常依赖于任务完成的时间。如果这对您很重要,那么您需要在完成线程或分叉后进行整理和排序。

如上所述,在threads文档中,通常可能更好地展望分叉:

  

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