Perl 可以同时进行线程和分叉。官方不推荐使用“线程”——这在很大程度上是因为它没有被很好地理解,而且——也许有点违反直觉——不像某些编程语言中的线程那样轻量级。
如果您特别热衷于线程,线程的“工作者”模型比为每个任务生成一个线程要好得多。您可能会在某些语言中使用后者——在 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 序列化。请注意,您的“结果”将在两者中以随机顺序排列,因为它在很大程度上取决于任务何时完成。如果这对您很重要,那么您需要在线程或分叉完成后进行整理和排序。
一般来说,分叉可能会更好 - 如上所述,在threads docs:
Perl 提供的“基于解释器的线程”并不是人们期望或希望的用于多任务处理的快速、轻量级系统。线程的实现方式使其易于误用。很少有人知道如何正确使用它们或能够提供帮助。
官方不鼓励在 perl 中使用基于解释器的线程。