Perl-потоки и небезопасные сигналы - PullRequest
1 голос
/ 30 августа 2010

Так что недавно я хотел создать поток для одной из моих программ на Perl, чтобы увеличить ее скорость. Взяв список веб-сайтов, я хотел создать ветку для каждого URL-адреса и получить содержимое каждого веб-сайта, а затем найти описание компании на странице. Как только один поток нашел результат, или все потоки не нашли, я захотел выйти, записать свой результат и прочитать в URL для моей следующей компании.

Проблема, которую я вижу, заключается в том, что я использую модуль Perl :: Unsafe :: Signals внутри функции, которую я вызываю при создании потока. Мне нужны небезопасные сигналы для прерывания регулярных выражений, которые «застревают». Однако это, кажется, вызывает всевозможные проблемы, в основном с аварийным завершением программы и отображением сообщения об ошибке «Будильник».

Следовательно, есть ли способ безопасно использовать Perl :: Unsafe :: Signals и thread? Есть ли способ тайм-аута регулярного выражения другим способом, посылая сигнал функции (как я посылаю сигнал '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
####### СУБРОУТИН, ВЫЗВАННЫЙ 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($@) {
        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

Ответы [ 3 ]

7 голосов
/ 31 августа 2010

Как правило, «небезопасные» сигналы небезопасны как для однопоточных, так и для многопоточных.Вы только увеличили свою опасность, используя небезопасные сигналы и .Обычный безопасный обработчик сигналов в Perl устанавливает флаг signal_pending без значительного прерывания выполнения.ВМ проверяет этот флаг, когда он находится между кодами операций.

Ваше выполнение регулярного выражения представляет собой один «атомарный» код операции.Конечно, само регулярное выражение является еще одной виртуальной машиной со своими собственными кодами операций, но в настоящее время мы не видим этого для обработчика сигналов 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;
}
2 голосов
/ 31 августа 2010

ИМХО, смешивание сигналов и потоков само по себе является сложной задачей (т.е. без специфических для perl вещей).Помните, что даже в однопоточной программе вы можете безопасно вызывать только функции, защищающие от асинхронного сигнала, из обработчика сигнала, потому что программа может быть прервана в любой момент.Perl добавляет еще один уровень абстракции, поэтому я не имею представления о безопасности вызова «die» из обработчика сигналов в случае небезопасных сигналов.

Если я правильно помню, SIGALRM - это асинхронный сигнал, поэтому он должен обрабатываться синхронно,Ваш способ обращения с ним, как правило, некорректен в многопоточных программах.

Более того, потоки IMHO для perl просто не работают, как большинство людей ожидают.Просто избегайте их использования и используйте вместо этого процессы.

PS

Следующая строка не имеет смысла:

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

SIGKILL (такжекак SIGSTOP) не может быть пойман.

1 голос
/ 31 августа 2010

Я не очень специалист по Perl-MT, но одна вещь, которую вы, очевидно, упускаете, это то, что сигналы являются глобальными для всего процесса - они не являются специфичными для потока.В системах POSIX вы не можете установить обработчик сигнала для потока: сигналы доставляются всему процессу.Вызов IOW alarm() влияет на весь процесс, а не только на поток, который его вызывает.И даже local %SIG в контексте MT не делает то, о чем можно подумать, потому что local - это синтаксис.

...