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);
Вам необходимоБудьте особенно осторожны с файловым вводом-выводом при многопроцессорной обработке, потому что весь смысл в том, что ваша последовательность выполнения более не определена.Так что безумно легко заканчивать тем, что разные потоки блокируют файлы, которые другой поток открыл, но не сбросил на диск.
Я отмечаю ваш код - вы, кажется, полагаете, что не удалось открыть файл, чтобы не печатать на нем.Это нехорошо, особенно если дескриптор файла не ограничен лексической областью.
Но в обеих парадигмах многопроцессорности, которые я обрисовал выше (есть и другие, они наиболее распространенные), вам все равно приходится иметь дело с сериализацией файлового ввода-вывода.Обратите внимание, что ваши «результаты» будут в случайном порядке в обоих случаях, потому что это будет очень сильно зависеть от того, когда задача завершится.Если это важно для вас, то вам нужно будет сопоставлять и сортировать после завершения ваших веток или веток.
Вероятно, в целом лучше смотреть на разветвление - как сказано выше, в threads
документах:
«Основанные на интерпретаторе потоки», предоставляемые Perl, не являются быстрыми и легкими.система многозадачности, которую можно ожидать или надеяться.Потоки реализованы таким образом, что их легко использовать неправильно.Мало кто знает, как правильно их использовать, или сможет оказать помощь.Использование потоков на основе интерпретатора в perl официально не рекомендуется.