我对perl(以及编程也是)很新,并且在过去的几周内一直在玩线程,到目前为止,我明白使用它们执行一些类似的并行任务是令人沮丧的 - 内存消耗是无法控制的如果您的线程数取决于某些输入值,并且只是限制该数字并进行一些临时连接似乎非常愚蠢。 所以我试图欺骗线程通过队列返回一些值,然后分离这些线程(并没有真正加入它们) - 这是一个并行ping的例子:
#!/usr/bin/perl
#
use strict;
use warnings;
use threads;
use NetAddr::IP;
use Net::Ping;
use Thread::Queue;
use Thread::Semaphore;
########## get my IPs from CIDR-notation #############
my @ips;
for my $cidr (@ARGV) {
my $n = NetAddr::IP->new($cidr);
foreach ( @{ $n->hostenumref } ) {
push @ips, ( split( '/', $_ ) )[0];
}
}
my $ping = Net::Ping->new("icmp");
my $pq = Thread::Queue->new( @ips, undef ); # ping-worker-queue
my $rq = Thread::Queue->new(); # response queue
my $semaphore = Thread::Semaphore->new(100); # I hoped this may be usefull to limit # of concurrent threads
while ( my $phost = $pq->dequeue() ) {
$semaphore->down();
threads->create( { 'stack_size' => 32 * 4096 }, \&ping_th, $phost );
}
sub ping_th {
$rq->enqueue( $_[0] ) if $ping->ping( $_[0], 1 );
$semaphore->up();
threads->detach();
}
$rq->enqueue(undef);
while ( my $alive_ip = $rq->dequeue() ) {
print $alive_ip, "\n";
}
我无法找到一个关于thread-> detach()应该如何在一个线程子程序中工作的完全全面的描述,并认为这可能有用......而且它确实 - 如果我在主要的东西做某事程序(线程)延长它的生命周期(睡眠做得很好),所以所有分离的线程完成并将它们的部分排入我的$ rq,否则它将运行一些线程将其结果收集到队列并退出并发出警告像:
Perl exited with active threads:
5 running and unjoined
0 finished and unjoined
0 running and detached
制作主程序"睡眠"有一段时间,再一次,似乎很愚蠢 - 有没有办法让线程做他们的东西,只在实际的线程 - > detach()调用后分离? 到目前为止,我的猜测是,一旦创建了线程,子线程中的threads-> detach()就会应用,所以这不是方法。 我用CentOSs老版本v5.10.1尝试了这个。这应该改变现代v5.16或v5.18(usethreads-compiled)?
答案 0 :(得分:6)
分离一个帖子并不是特别有用,因为你有效地说'他们退出时我不在乎'。
这通常不是您想要的 - 您的流程在线程仍在运行时完成。
通常情况下 - 创建线程会产生开销,因为您的进程被克隆在内存中。你想避免这样做。 Thread::Queue
也很好用,因为它是一种传递信息的线程安全方式。在您的代码中,$pq
实际上并不需要它,因为您实际上并未在使用它时进行线程处理。
你的信号量是这样做的一种方法,但我可以建议作为替代方法:
#!/usr/bin/perl
use strict;
use warnings;
use Thread::Queue;
my $nthreads = 100;
my $ping_q = Thread::Queue -> new();
my $result_q = Thread::Queue -> new();
sub ping_host {
my $pinger = Net::Ping->new("icmp");
while ( my $hostname = $ping_q -> dequeue() ) {
if ( $pinger -> ping ( $hostname, 1 ) ) {
$result_q -> enqueue ( $hostname );
}
}
}
#start the threads
for ( 1..$nthreads ) {
threads -> create ( \&ping_host );
}
#queue the workload
$ping_q -> enqueue ( @ip_list );
#close the queue, so '$ping_q -> dequeue' returns undef, breaking the while loop.
$ping_q -> end();
#wait for pingers to finish.
foreach my $thr ( threads -> list() ) {
$thr -> join();
}
$results_q -> end();
#collate results
while ( my $successful_host = $results_q -> dequeue_nb() ) {
print $successful_host, "\n";
}
这样,您可以预先生成线程,对目标进行排队,然后在完成后整理结果。您不会产生重复重新生成线程的开销,并且您的程序将等待所有线程完成。这可能有一段时间了,因为'down'主机上的ping超时将会持续一段时间。
答案 1 :(得分:1)
由于无法连接分离的线程,您可以等待线程完成其工作,
sleep 1 while threads->list();