使用python管理事件循环的早期返回

时间:2018-11-26 21:26:49

标签: python-3.x monitoring python-asyncio

我有运行以下循环的服务

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中的事件循环听起来像是要走的路。但是由于我的经验不足,我不知道checkbreak逻辑在事件循环中应该走到哪里。

或者还有其他软件包/代码模式可以执行这种监视逻辑吗?

1 个答案:

答案 0 :(得分:2)

在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中进行此操作。一项取消其他任务将确保仅存在一项反馈。