Подтвердить что ты не робот

Каков правильный способ обработки (в python) IOError: [Errno 4] Прерванный системный вызов, вызванный multiprocessing.Queue.get

Когда я использую multiprocessing.Queue.get, я иногда получаю исключение из-за EINTR.

Я определенно знаю, что иногда это происходит без уважительной причины (я открываю другую панель в tmux buffr), и в таком случае я бы хотел продолжить работу и повторить операцию.

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

Как я могу различить два?

Заранее спасибо

4b9b3361

Ответ 1

Ошибка EINTR может быть возвращена из многих системных вызовов, когда приложение получает сигнал, ожидая другого ввода. Обычно эти сигналы могут быть довольно мягкими и уже обрабатываться Python, но основной системный вызов по-прежнему заканчивается прерыванием. При кодировании C/С++ это одна из причин, почему вы не можете полностью полагаться на такие функции, как sleep(). Библиотеки Python иногда обрабатывают этот код ошибки внутренне, но, очевидно, в этом случае это не так.

Вам может быть интересно прочитать этот поток, который обсуждает эту проблему.

Общий подход к EINTR состоит в том, чтобы просто обработать ошибку и снова повторить операцию - это должно быть безопасным способом использования метода get() в очереди. Возможно, что-то подобное, передавая очередь в качестве параметра и заменяя использование метода get() в очереди:

import errno

def my_queue_get(queue, block=True, timeout=None):
    while True:
        try:
            return queue.get(block, timeout)
        except IOError, e:
            if e.errno != errno.EINTR:
                raise

# Now replace instances of queue.get() with my_queue_get(queue), with other
# parameters passed as usual.

Обычно вам не нужно беспокоиться о EINTR в программе Python, если вы не знаете, что ожидаете какой-либо конкретный сигнал (например, SIGHUP), и вы установили обработчик сигнала, который устанавливает флаг и опирается на основную часть кода, чтобы поднять флаг. В этом случае вам может потребоваться выйти из цикла и проверить флаг сигнала, если вы получили EINTR.

Однако, если вы не используете какую-либо обработку сигналов, вы должны просто игнорировать EINTR и повторять свою операцию - если сам Python должен что-то сделать с сигналом, который он должен был обработать в сигнале обработчик.

Ответ 2

Старый вопрос, современное решение: с Python 3.5 реализовано замечательное PEP 475 - системные вызовы с повторными попытками с EINTR и решает проблема для тебя. Вот тезис:

Обертки системного вызова, предоставляемые в стандартной библиотеке, должны быть автоматически перезапущены, если они не сработали с EINTR, чтобы освободить код приложения от бремени этого.

По системным вызовам мы имеем в виду функции, открытые стандартной библиотекой C, относящиеся к I/O или обработке других системных ресурсов.

В принципе, система будет ловить и повторить для вас фрагмент кода, который не прошел с EINTR, поэтому вам больше не придется его обрабатывать. Если вы настроили таргетинг на более раннюю версию, цикл while True все еще является способом перехода. Обратите внимание, что если вы используете Python 3.3 или 3.4, вы можете поймать выделенное исключение InterruptedError вместо ловли IOError и проверить на EINTR.