управлять ранним возвратом цикла событий с помощью Python - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть служба, выполняющая следующий цикл

while True:
    feedback = f1()
    if check1(feedback):
        break

    feedback = f2()
    if check2(feedback):
        break

    feedback = f3()
    if check3(feedback):
        break

    time.sleep(10)

do_cleanup(feedback)

Теперь я хотел бы запустить эти проверки обратной связи с разными временными интервалами.Один наивный способ - переместить time.sleep() в функции f.Но это вызывает блокировку.Какой самый простой способ добиться периодических проверок с разными интервалами?Здесь все функции f дешевы в эксплуатации.

Цикл событий в asyncio звучит как путь.Но из-за моей неопытности я не знаю, где логика check и break должна идти в цикле событий.

Или есть какие-либо другие пакеты / шаблоны кода для выполнения такой логики мониторинга?

1 Ответ

0 голосов
/ 27 ноября 2018

В asyncio вы можете разделить службу на три отдельные задачи, каждая со своим собственным циклом и временем - вы можете думать о них как о трех потоках, за исключением того, что они все запланированы в одном потоке, и многозадачность совместно выполняется путем приостановки наawait.

Для этого давайте начнем с служебной функции, которая вызывает функцию и проверяет ее результат через регулярный интервал:

async def at_interval(f, check, seconds):
    while True:
        feedback = f()
        if check(feedback):
            return feedback
        await asyncio.sleep(seconds)

return является эквивалентомbreak в исходном коде.

С этим на месте служба порождает три таких цикла и ждет завершения любого из них.То, что завершится первым, несет ожидаемую «обратную связь», и мы можем распоряжаться остальными.

async def service():
    loop = asyncio.get_event_loop()
    t1 = loop.create_task(at_interval(f1, check1, 3))
    t2 = loop.create_task(at_interval(f2, check2, 5))
    t3 = loop.create_task(at_interval(f3, check3, 7))
    done, pending = await asyncio.wait(
        [t1, t2, t3], return_when=asyncio.FIRST_COMPLETED)
    for t in pending:
        t.cancel()
    feedback = await list(done)[0]
    do_cleanup(feedback)

asyncio.get_event_loop().run_until_complete(service())

Небольшая разница между этим и вашим кодом заключается в том, что здесь это возможно (хотя и маловероятно)более чем одна проверка не пройдена до того, как service обнаружит ее.Например, если из-за неудачи две из вышеперечисленных задач в итоге разделят абсолютное время пробуждения с микросекундой, они будут запланированы в одной и той же итерации цикла событий.Оба вернутся из своих сопрограмм at_interval, а done будет содержать более одного отзыва.Код обрабатывает его, выбирая обратную связь и вызывая do_cleanup для этого, но он также может зацикливаться на всех.

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

...