2010-08-30 71 views
1

所以我最近想要通過一個Perl程序來提高速度。考慮到網站列表,我想爲每個網址開始一個線索並獲取每個網站的內容,然後在頁面上查找公司描述。一旦一個線程找到了結果,或者所有線程都沒有,我想退出,寫下我的結果,然後閱讀我的下一個公司的URL。Perl線程和不安全信號

我看到的問題是我在創建線程時調用的函數內部使用了Perl :: Unsafe :: Signals模塊。我需要不安全的信號來中斷正在被卡住的正則表達式。但是,這似乎會導致各種各樣的問題,主要是程序崩潰,並顯示錯誤消息「鬧鐘」。

因此,有沒有一種方法可以安全地使用Perl :: Unsafe :: Signals和線程?有沒有辦法通過向函數發送信號以另一種方式超時正則表達式(例如,我在下面發送'KILL'信號?)謝謝。

注:我將代碼分解到所有相關部分,讓我知道你是否需要更多。

use threads ('exit' => 'threads_only'); 
use threads::shared; 
my @descrip; 
share(@descrip); 

my $lock; 
share($lock); 

URL:foreach my $url(@unique_urls) { 
     #skip blank urls 
     if(!$url) { next URL; }#if 

     #find description 
     my $thread = threads->create(\&findCompanyDescription, $PREV_COMPANY, $PREV_BASE_URL, $url); 

#while a description has not been found and there are still active threads, keep looking 
#there may be a better way to do this, but this seems to work for me 
while([email protected] && threads->list() != 0) {;} 

#kill all threads, write output, read in next batch of urls 
my @threads = threads->list(); 
foreach(@threads) { print("detaching\n"); $_->kill('KILL')->detach(); }#foreach 
####### SUBROUTINE CALLED BY THREAD CREATE
sub findCompanyDescription { 
    my($company_full, $base_url, $url) = @_; 
    my($descrip, $raw_meta, $raw) = ''; 
    my @company; 

    $SIG{'KILL'} = sub { alarm(0); threads->exit(); }; 

    eval { 
     local $SIG{ALRM} = sub { die("alarm\n") }; # NB: \n required 
     alarm(5); 

     use Perl::Unsafe::Signals; 
     UNSAFE_SIGNALS { 

      while($company) { 
      my @matches = ($content =~ m!.*<([\w\d]+).*?>\s*about\s+$company[\w\s\-_]*<.*?>(?:<.*?>|\s)*(.*?)</\1.*?>!sig); 

      MATCH:for(my $ndx=1; $ndx<@matches; $ndx+=2) { 
      ($raw, $descrip) = &filterResult($matches[$ndx], $company_full); 

      if($descrip) { 
       $company = undef; 
       last(MATCH); 
      }#if 
     }#for 

     #reduce the company name and try again 
     $company = &reduceCompanyName($company); 

     }#while 

     alarm(0); 
     };#unsafe_signals 
    };#eval 

    if([email protected]) { 
     if([email protected] eq "alarm\n" && $DEBUG) { print("\nWebpage Timeout [].\n"); }#if 
    }#if 

    if($descrip) { lock($lock); { 
     @descrip = ($PREV_ID, $company_full, $base_url, $url, 1, $raw, $descrip); } 
    }#if 
+0

小心提及您的平臺? – Dummy00001 2010-08-31 12:15:49

+0

Mac OS X雪豹,有時是Fedora Core 8. – user387049 2010-08-31 15:59:19

回答

7

一般而言, 「不安全」 的信號是不安全的兩個單線程和多線程。您只會通過使用線程不安全的信號增加了您的危險。 Perl的通常的安全信號處理程序設置標誌signal_pending而沒有有意義的中斷執行。 VM在操作碼之間檢查該標誌。

您的正則表達式執行是一個單一的「原子」操作碼。當然,正則表達式本身是另一個具有自己的操作碼的虛擬機,但是我們目前對perl信號處理程序沒有可見性。

坦率地說,我不知道如何中斷正則表達式引擎。它有一些全球C狀態,在過去的perl-5.10之前,它阻止了它的重入。像您正在嘗試的那樣,通用中斷性可能並不安全。如果你真的希望它是完全可中斷的,你可能想要分叉並讓你的子進程執行正則表達式,並通過管道傳回結果。

require JSON; 
require IO::Select; 

my $TIMEOUT_SECONDS = 2.5; # seconds 

my ($read, $write); 
pipe $read, $write; 

my @matches; 
my $pid = fork; 
if ($pid) { 

    my $select = IO::Select->new($read); 
    if ($select->can_read($TIMEOUT_SECONDS)) { 
     local $/; 
     my $json = <$read>; 
     if ($json) { 
      my $matches_ref = JSON::from_json($json); 
      if ($matches_ref) { 
       @matches = @$matches_ref; 
      } 
     } 
    } 
    waitpid $pid, 0; 
} 
else { 
    my @r = $content =~ m!.*<([\w\d]+).*?>\s*about\s+$company[\w\s\-_]*<.*?>(?:<.*?>|\s)*(.*?)</\1.*?>!sig; 
    my $json = JSON::to_json(\ @r); 
    print { $write } $json; 
    close $write; 
    exit; 
} 
+1

如果我真的想要一個可中斷的正則表達式引擎,我可以嘗試使用不同的引擎,如[http://perldoc.perl.org/perlreapi.html]。或者...我可能嘗試在進程間調用中將perl regexp引擎嵌入到可插入的正則表達式引擎中,以便上述想法將僅僅是「僅」聲明上面的正則表達式有望在劣質進程中進行評估並被中止隨意。 – 2010-08-30 23:45:19

+0

您對使用哪種引擎有任何建議嗎? – user387049 2010-09-08 15:01:23

2

恕我直言,混合信號和線程本身是一項具有挑戰性的任務(即沒有特定的東西)。 請記住,即使在單線程程序中,您也可以安全地僅從信號處理程序調用異步信號安全函數,因爲程序可能會在任何時候中斷。 Perl增加了另一層抽象層,所以我不知道在信號不安全的情況下從信號處理器調用「死」的安全性。

如果我沒記錯,SIGALRM是異步信號,所以必須同步處理。多線程程序中處理它的方式通常不正確。

此外,恕我直言perl線程不會像大多數人所期望的那樣工作。 只需避免使用它們並使用進程。

P.S.

以下行沒有任何意義:

$SIG{'KILL'} = sub { alarm(0); threads->exit(); };

SIGKILL(以及SIGSTOP)不能被捕獲。

+0

我得到了(不是警報部分,正在嘗試其他的東西)從perl文檔@ http://perldoc.perl.org/threads.html – user387049 2010-08-30 22:03:28

1

我並不是真正的Perl-MT專家,但是你顯然缺少的一件事是信號在整個過程中是全局性的 - 它們不是線程特定的。在POSIX系統上,您不能爲線程設置信號處理程序:信號傳遞到整個進程。調用影響整個過程,而不僅僅是調用它的線程。即使在MT環境中的local %SIG也不會做人們可能會認爲它的做法 - 因爲local是語法問題。