Как вы засыпаете в обратном вызове AnyEvent, не приостанавливая весь скрипт?

Я специально использую AnyEvent::Inotify::Simple (используя EV ​​в качестве бэкенда) для мониторинга событий файла. Итак, у меня есть обратный вызов обработчика, который в конкретном случае мне нужно «приостановить», прежде чем продолжить. sleep очевидно останавливает все (поэтому другие события не обрабатываются до тех пор, пока не будет выполнено sleep), поэтому я попытался использовать alarm, однако, если возникает тот же сценарий, который снова устанавливает сигнал тревоги, исходный сигнал тревоги (и, следовательно, его $SIG{ALRM} sub) игнорируется и запускается только "новый".

#sleep-based approach (definitely a no-go)
sub handler {
    my ( $self, $event, $file ) = @_;
    #do some stuff
    ...
    if( $some_condition_exists ) {
        sleep(3);
        #now that we've waited, if some change occurred then do stuff
        if ( $new_change_occurred ) {
            #do some new stuff
        }
    }
    return;
}

#alarm -based approach (still a no-go when callback is called again while original alarm is still counting down)
sub handler {
    my ( $self, $event, $file ) = @_;
    #do some stuff
    ...
    if( $some_condition_exists ) {
        $SIG{ALRM} = sub {
             #now that we've waited, if some change occurred then do stuff
             if ( $new_change_occurred ) {
                 #do some new stuff
             }
        }
        alarm(3);
    }
    return;
}

Любой совет будет принят во внимание? Все, что мне нужно, — это способ приостановить обратный вызов, продолжая асинхронно обрабатывать новые события inotify.


person FunkyShu    schedule 15.01.2015    source источник
comment
Используйте $cv->recv, чтобы заснуть, и сделайте $cv->send, когда произойдет изменение.   -  person ikegami    schedule 15.01.2015
comment
Вы используете use AE::timer(3, $cv); для предоставления тайм-аута.   -  person ikegami    schedule 15.01.2015
comment
AnyEvent-›timer(), вероятно, был правильным подходом, и я уже пробовал его, но так и не заставил его работать из обратного вызова AnyEvent::Inotify::Simple event_receiver. Я не уверен, что это как-то конфликтовало с запуском цикла EV (связанным с inotify) или просто с неправильным использованием. Во всяком случае, мое решение заключалось в использовании POE::Session delay_add. Кажется немного безумным вводить еще одну рамку событий (особенно такую ​​громоздкую) в то же приложение, но это сработало. Я рассматривал возможность использования POEx::Inotify вместо AnyEvent::Inotify::Simple, но это означало бы значительную переработку моего приложения. Спасибо за помощь!   -  person FunkyShu    schedule 19.02.2015
comment
Если вы находитесь в recv, очередь событий будет проверена на наличие события таймера, поэтому его можно использовать для тайм-аута.   -  person ikegami    schedule 19.02.2015


Ответы (1)


При использовании AnyEvent::timer убедитесь, что вы отслеживаете объект защиты, который он возвращает. Обычно я храню их в файле global.

Если вы присвоите его переменной, которая выходит за рамки, таймер будет отменен.

my $guards;

sub handler {
    my ( $self, $event, $file ) = @_;
    #do some stuff
    ...
    if( $some_condition_exists ) {
        $guards->{$file} = AE::timer 3,0,sub {
             #now that we've waited, if some change occurred then do stuff
             if ( $new_change_occurred ) {
                 #do some new stuff
             }
        };
    }
    return;
}
person TheAmigo    schedule 04.05.2015
comment
Ага, именно это и происходило. Таймер вышел из области видимости, когда обратный вызов обработчика вернулся и умер тихой смертью. Крутой момент, но спасибо, что указали на него! - person FunkyShu; 18.06.2015