Тайм-аут и функция исключения - PullRequest
2 голосов
/ 05 марта 2019

Я опрашиваю данные, используя функцию Python 2.7.10, для которой я хочу установить таймаут, если устройство слишком долго отвечает, или перехватить RuntimeError, если это устройство недоступно. Я использую эту функцию Timeout:

class Timeout():
        class Timeout(Exception):
            pass
        def __init__(self, sec):
            self.sec = sec
        def __enter__(self):
            signal.signal(signal.SIGALRM, self.raise_timeout)
            signal.alarm(self.sec)
        def __exit__(self, *args):
            signal.alarm(0)
        def raise_timeout(self, *args):
            raise Timeout.Timeout()

Это мой цикл для опроса данных (Modbus) и определения исключений. Этот цикл вызывается каждые 60 секунд:

def getDeviceTags(name, tag_data):
    global val_returns
    for tag in tag_data[name]:
        local_vals = []
        local_vals.append(name+"."+tag)
        try:
            with Timeout(3):
                value = modbus.read(str(name), str(tag))
                local_vals.append(str(value.value()))
        except RuntimeError:
            print("RuntimeError on " + str(name))
            local_vals.append(None)
        except Timeout.Timeout:
            print("Timeout on " + str(name))
            local_vals.append(None)
        val_returns.append(local_vals)

Это будет работать в течение ДНЕЙ без проблем, как RuntimeErrors и Timeouts печатаются в консоль, все данные записываются - GREAT. Тем не менее, в последнее время он застрял - и это единственная ошибка, которую я получаю:

Traceback (most recent call last):
  File "working_one_min_back.py", line 161, in <module>
    job()
  File "working_one_min_back.py", line 79, in job
    getDeviceTags(str(key), data)
  File "working_one_min_back.py", line 57, in getDeviceTags
    print("RuntimeError on " + str(name))
  File "working_one_min_back.py", line 30, in raise_timeout
    raise Timeout.Timeout()
__main__.Timeout

1 Ответ

0 голосов
/ 06 марта 2019

Нет гарантии, что «сигнал Python» не будет доставлен после вызова alarm(0).Фактический (C) сигнал может быть уже доставлен, в результате чего обработчик Python будет вызван несколькими инструкциями байт-кода позже.

Если вы вызываете signal.signal из__exit__, любой такой ожидающий сигнал отбрасывается , что не позволяет принять его за запрашиваемый следующий .Использование этого для восстановления обработчика до значения, которое оно имело до создания Timeout (как возвращено первым вызовом signal.signal), в любом случае является хорошей идеей.(Сбросьте после вызова alarm(0), чтобы SIG_DFL не убил процесс.)

В Python 3 такой вызов доставляет любые ожидающие сигналы вместоотбрасывание их, что является улучшением в том смысле, что предотвращает потерю сигнала только потому, что изменился обработчик.(К сожалению, это не более задокументировано, чем поведение Python 2. К сожалению, вы можете попытаться подавить такой поздний сигнал, установив атрибут в __exit__ и проигнорировав любой (Python) сигнал, возникающий при его установке.

Конечно, сигнал может быть доставлен после того, как __exit__ начнет выполнение и до того, как сигнал будет сброшен (или помечен как игнорируемый).Поэтому вам нужно обработать операцию и для завершения и тайм-аута, возможно, имея несколько присваиваний одной переменной, которая затем append редактируется только в одном месте.

...