2014-10-29 48 views
3

我很新的perl(也是编程),并且在过去的几个星期里在线程上玩耍,到目前为止我明白使用它们来执行一些类似的并行任务是不鼓励的 - 内存消耗如果你的线程数量取决于某些输入值,并且仅仅限制这个数字并进行一些临时连接似乎非常愚蠢,那么它是不可控制的。 所以我试图欺骗线程队列通过,随后分离这些线程(和没有实际加入他们)返回我一些价值观 - 在这里与平行平的例子:perl线程自我分离

#!/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"; 
} 

我找不到一个完全关于threads-> detach()应该如何在一个线程化子程序中工作,并认为这可能会奏效......并且它的确如此 - 如果我在主程序(线程)中做了一些延伸它一生的工作(睡眠很好) ,因此所有分离的线程完成并将它们的部分排入我的$ rq,否则它将运行一些线程将其结果收集到队列并退出,并出现如下警告:

Perl exited with active threads: 
    5 running and unjoined 
    0 finished and unjoined 
    0 running and detached 

让主程序“睡眠”一段时间再次显得很愚蠢 - 是否没有办法使线程完成它们的工作,并在实际线程 - > detach()调用之后进行分离? 到目前为止,我的猜测是一旦创建线程就会应用子线程中的threads-> detach(),所以这不是方法。 我用CentOS很好的旧v5.10.1试了一下。应该用现代v5.16还是v5.18(usethreads-compiled)进行更改?

+1

顺便说一句,为什么你想分离线程? – 2014-10-29 12:08:55

+0

我有些困惑。如果你想让线程更快分离,请尽快调用'threads-> detach()'(并不是说我明白为什么要分离线程开始)。此外,你可能会产生少量的工作线程,让他们出列,而不是每个排队的元素产生一个线程。这样你就可以拥有更少的线程,并且可以推迟加入直到工作完成(例如,等待直到'!$ pq-> pending()',或者只要加入队列中的所有线程就加入池中的所有线程工作) – Hasturkun 2014-10-29 12:29:20

+0

我并不认为detach实际上是这里工作的工具,因为你在尝试整理结果时需要等待每个线程退出前完成。 – Sobrique 2014-10-29 12:38:52

回答

1

由于超脱线程不能加入,你可以等待线程完成自己的工作,

sleep 1 while threads->list(); 
+4

虽然我会建议 - 重新做到这一点 - 那么它可能很容易让_not_分离线程并加入它们。 – Sobrique 2014-10-29 12:39:19

6

卸下一个线程是不是特别有用,因为你实际上是说:“我不当他们退出时关心'。

这通常不是您想要的 - 您的过程在线程仍在运行时完成。

通常虽然 - 创建线程有开销,因为你的进程克隆在内存中。你想避免这样做。 Thread::Queue也很好用,因为它是传递信息的线程安全方式。在你的代码中,你实际上并不需要它,因为你并不是真的在使用它的地方进行线程化。

你的信号是一个方法做它,但我可以建议作为一种替代方案:

#!/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

谢谢!这改变了我对线程安静的看法! – depebo 2014-10-29 16:06:50

+1

虽然我仍然不清楚** $ q-> end()**和** $ q-> enqueue(undef)**之间的区别** - 我以为这些是相同的,但不知何故我的线程wouldn'除非我结束() - 编辑我的队列 – depebo 2014-10-29 16:14:10

+1

你需要'入队()'更多'undef's。否则,第一个到达它的线程会将其移出队列,其他线程将阻止等待更多元素通过队列。 'end'关闭一个队列,这样'dequeue'不会被阻塞,只是返回'undef'。 – Sobrique 2014-10-29 16:30:38