1

そのため、最近、Perl プログラムの 1 つをスレッド化して速度を上げたいと考えました。Web サイトのリストを取得して、各 URL のスレッドを開始し、各 Web サイトのコンテンツを取得してから、ページで会社の説明を探したいと考えました。1 つのスレッドが結果を見つけた場合、またはすべてのスレッドが結果を見つけなかった場合、私は終了し、結果を書き、次の会社の URL を読みたいと思いました。

私が見ている問題は、スレッドを作成するときに呼び出す関数内で 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(!@descrip && 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
#######スレッド作成によって呼び出されるサブルーチン
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($@) {
        if($@ 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
4

3 に答える 3

7

一般に、「安全でない」シグナルは、シングルスレッドとマルチスレッドの両方で安全ではありません。スレッドと安全でないシグナルを使用することで、危険が増しただけです。signal_pendingPerl の通常の安全なシグナル ハンドラは、意味のある実行の中断なしにフラグを設定します。オペコード間にある場合、VM はそのフラグをチェックします。

正規表現の実行は、単一の「アトミック」オペコードです。もちろん、正規表現自体は独自のオペコードを持つ別の VM ですが、現在、perl シグナル ハンドラーの可視性はありません。

率直に言って、正規表現エンジンを中断する方法がよくわかりません。過去に perl-5.10 より前の再入可能を妨げていたいくつかのグローバルな C 状態があります。あなたが試みているように、普遍的な割り込み可能性に対して安全ではないかもしれません。本当に完全に中断可能にしたい場合は、フォークして子プロセスに正規表現を実行させ、パイプを介して結果を通信することをお勧めします。

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;
}
于 2010-08-30T20:53:24.297 に答える
2

IMHO、シグナルとスレッドの混合は、それ自体が困難な作業です(つまり、perl固有のものがない場合)。シングルスレッドプログラムでも、プログラムはいつでも中断される可能性があるため、シグナルハンドラからasync-signal-safe関数のみを安全に呼び出すことができることに注意してください。Perlは抽象化の別のレイヤーを追加するので、安全でないシグナルの場合にシグナルハンドラーから「die」を呼び出すことの安全性についてはわかりません。

私の記憶が正しければ、SIGALRMは非同期信号であるため、同期的に処理する必要があります。マルチスレッドプログラムでは、一般的にそれを処理する方法は正しくありません。

さらに、IMHOのperlスレッドは、ほとんどの人が期待するようには機能しません。それらの使用を避け、代わりにプロセスを使用してください。

PS

次の行は意味がありません。

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

SIGKILL(およびSIGSTOP)をキャッチできません。

于 2010-08-30T21:31:35.237 に答える
1

私は Perl-MT の専門家ではありませんが、シグナルがプロセス全体に対してグローバルであり、スレッド固有ではないということを見逃しているようです。POSIX システムでは、スレッドにシグナル ハンドラーを設定することはできません。シグナルはプロセス全体に配信されます。IOWalarm()呼び出しは、それを呼び出したスレッドだけでなく、プロセス全体に影響します。そしてlocal %SIG、MT のコンテキストでさえ、人が思っているようなことをしませんlocal。構文の問題だからです。

于 2010-08-31T12:18:19.127 に答える