2012-08-25 169 views
1

我有下面的子例程,並有APPX 20個線程具有不同的URL調用它(這一分屬於一個包,每個線程調用封裝的不同實例):AnyEvent在多線程環境

sub get_urls { 
    my ($self,$url,$depth) = @_; 
    my $cv = AnyEvent->condvar; 
    my @data; 
    my %visited; 
    my $hostname = URI->new($url)->host(); 
    my $tr_cb; 
    my ($b,$e) = (0,0); 

     return unless($depth); 
     # This code-ref is recursive! 
     $tr_cb = sub { 
     my $sitem = shift; 
     my $depth = shift; 

     return if (0 == $depth--); 
     foreach my $site (@$sitem) { 
      if (exists($visited{$site})) { 
       next; 
      } 
      $b++; 
      $visited{$site} = 1; 
      $cv->begin; 
      AnyEvent::HTTP::http_get ($site, timeout => 1, sub { 
       my ($body, $hdr) = @_; 
       if ($hdr->{Status} =~ m/^2/) { 
        my $extor = HTML::SimpleLinkExtor->new(); 
        my @links; 
        print "E = $e | B = $b\n"; 
        #print "[REC_DEPTH:$depth]Working on $site\n"; 
        $extor->parse($body); 
        @links = map { URI->new_abs($_,$site)->as_string } 
          grep { length > 2 } $extor->links(); 
        push(@data,@links); 
        $tr_cb->([map { $_->[2] } 
           grep { $_->[0] eq $_->[1] } 
           map { [$hostname,URI->new($_)->host(),$_] } @links],$depth); 
       } 
       $e++; 
       $cv->end; 
      }); 
     } 
     }; 
    $tr_cb->([$url],$depth); 
    $cv->recv; 
    print "Got total of " . @data . " links\n"; 
} 

($b,$e)變量僅用於測試。 問題是,經過一段時間,似乎'開始'的數量與'結束'的數量不匹配,因此它永遠不會通過$cv->recv ... 我對AnyEvent和事件編程有點新,似乎不能解決我的問題。

感謝,

+0

你爲什麼將線程與異步的事件驅動庫結合起來?只要您不遞歸recv,就可以在單線程應用程序中創建多個condvars。 – MkV

+0

很好的問題,實際上我的意思是流程,不是線程,它是一個錯字,我分叉,但我認爲它是錯誤的。 – snoofkin

回答

3

整個遞歸匿名子對於自己的好處似乎有點太聰明瞭。在你的類中創建一個函數,該函數從URL(和深度)中傳遞鏈接並將它們添加到對象的數組中。同時創建一個計時器(在=> 0之後),將元素從陣列中移出,如果仍有元素則重新啓動,否則發送結束符到condvar。如果需要,用Thread :: Queue對象替換數組。

你也應該只在你的應用程序代碼中調用 - > recv,而不是你的庫,或者在你的condvar上使用回調而不是調用recv(這將使你可以使用多個condvars併發送給它們而不依賴於線程)

-2

你有$cv->end;回調中。看來我錯了,應該是AnyEvent::HTTP::http_get打電話後,AFAIU。

+1

不,'$ cv-> end'在回調中的正確位置。 – dolmen

1

此效用函數設計中的錯誤是它在condvar上被阻塞:$cv->recv。這會阻止函數的用戶在全局異步程序中使用它。

取而代之的是,get_urls應該返回condvar($cv)並讓用戶做一些有用的事情。如果用戶想阻止,他將能夠做到。如果他不這樣做,他可以自由地與其他異步任務共享資源。

你的程序的另一個問題是,如果該函數的設計正確,你可能不需要線程:如果該函數是程序的核心,它顯然是網絡綁定的,而不是CPU綁定的,所以你應該能夠啓動多個調用(固定)get_urls,這些調用將在單個線程中並行執行。

+0

它不是問題,這是我期望它做的,不希望用戶在這種情況下決定這一點,異步部分僅用於獲取請求。但謝謝指出。 – snoofkin

+0

順便說一句,另一件事,可能是一個過於普遍的問題,但我會試一試,是否有理由在異步應用程序中產生(fork)新進程?我的意思是,這個函數是在一個新的進程中調用的,一個爬蟲的進程,每個爬蟲獲得一個新進程,幾個並行運行。 – snoofkin

+0

你必須確定是什麼讓你的程序變慢。如果沒有,在那裏停下來。如果這是網絡,那麼停下來,多個進程都無濟於事。如果這是CPU(程序運行的進程爲100%),那麼考慮多進程或多線程。但在一個人口衆多的情況下,我懷疑網絡是第一個問題。 – dolmen