2014-10-29 42 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