Обновите. Эту проблему можно решить с помощью исправлений, представленных в https://github.com/zbentley/AnyEvent-Impl-Perl-Improved/tree/io-starvation
Контекст:
Я интегрирую AnyEvent с другим синхронным кодом. Синхронный код должен установить некоторые наблюдатели (на таймеры, дочерние процессы и файлы), дождаться завершения хотя бы одного наблюдателя, выполнить некоторые синхронные/блокирующие/устаревшие действия и повторить.
Я использую основанный на чистом perl AnyEvent::Loop
цикл обработки событий, который на данный момент достаточно хорош для моих целей; большая часть того, что мне нужно, это отслеживание сигналов/процессов/таймеров.
Проблема:
Если у меня есть обратный вызов, который может на мгновение заблокировать цикл событий, события/обратные вызовы дочернего процесса-выход никогда не срабатывают. Самый простой пример, который я мог бы сделать, это наблюдать за дочерним процессом и запускать интервальный таймер. Интервальный таймер делает что-то блокирующее, прежде чем он завершится:
use AnyEvent;
# Start a timer that, every 0.5 seconds, sleeps for 1 second, then prints "timer":
my $w2 = AnyEvent->timer(
after => 0,
interval => 0.5,
cb => sub {
sleep 1; # Simulated blocking operation. If this is removed, everything works.
say "timer";
},
);
# Fork off a pid that waits for 1 second and then exits:
my $pid = fork();
if ( $pid == 0 ) {
sleep 1;
exit;
}
# Print "child" when the child process exits:
my $w1 = AnyEvent->child(
pid => $pid,
cb => sub {
say "child";
},
);
AnyEvent->condvar->recv;
Этот код оставляет дочерний процесс зомбированным и печатает «таймер» снова и снова, «навсегда» (я запускал его несколько минут). Если вызов sleep 1
удален из обратного вызова для таймера, код работает правильно, и наблюдатель дочернего процесса срабатывает, как и ожидалось.
Я ожидаю, что наблюдатель за детьми в конце концов запустится (в какой-то момент после того, как ребенок выйдет, и любые интервальные события в очереди событий будут запущены, заблокированы и завершены), но это не так.
sleep 1
может быть любой блокирующей операцией. Его можно заменить занятым ожиданием или любой другой вещью, которая занимает достаточно много времени. Это даже не должно занять секунду; кажется, что это должно быть только а) запущено во время события дочернего выхода/доставки SIGCHLD и б) привести к тому, что интервал всегда должен запускаться в соответствии с настенными часами.
Вопросы:
Почему AnyEvent никогда не запускает обратный вызов моего дочернего процесса?
Как я могу мультиплексировать события выхода дочернего процесса с интервальными событиями, которые могут блокироваться так долго, что наступит следующий интервал?
Что я пробовал:
Моя теория заключается в том, что события таймера, которые становятся «готовыми» из-за времени, проведенного вне цикла событий, могут на неопределенный срок опережать другие типы готовых событий (например, наблюдатели за дочерними процессами) где-то внутри AnyEvent. Я пробовал несколько вещей:
- Использование
AnyEvent::Strict
не выявляет ошибок и не изменяет поведение каким-либо образом. - Частичное решение: удаление интервального события в любой момент действительно приводит к срабатыванию наблюдателя дочернего процесса (как если бы внутри AnyEvent выполнялся какой-то внутренний опрос событий/заполнение очереди, что происходит только в том случае, если нет уже готовых событий таймера). "по настенным часам). Недостатки: в общем случае это не работает, так как мне нужно знать, когда мой дочерний процесс завершился, чтобы знать, когда отложить мои интервалы, что является тавтологией.
- Частичное решение: в отличие от наблюдателей за дочерними процессами, другие интервальные таймеры, по-видимому, могут прекрасно мультиплексироваться друг с другом, поэтому я могу установить ручной вызов
waitpid
в другом интервальном таймере, чтобы проверять и собирать дочерние процессы. Недостатки: ожидание дочерних элементов может быть искусственно отложено (мой вариант использования включает в себя множество частых процессов создания/уничтожения), любыеAnyEvent::child
наблюдатели, которые установлены и успешно срабатывают, автоматически пожинают дочерние элементы и не сообщают мой интервал /waitpid timer, требующий оркестровки, и обычно мне кажется, что я неправильно использую AnyEvent.
AnyEvent
сказано следующее Это означает, что вы не можете создать дочерний наблюдатель в качестве самого первого элемента в программе AnyEvent, вы должны создать по крайней мере один наблюдатель, прежде чем разветвлять дочерний элемент. - person Borodin   schedule 12.06.2016