代码之家  ›  专栏  ›  技术社区  ›  nos

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

  •  2
  • nos  · 技术社区  · 5 年前

    我有一个服务运行以下循环

    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 回复  |  直到 5 年前
        1
  •  2
  •   user4815162342    5 年前

    在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 在那一个,但它也可以循环所有。

    如果这是不可接受的,你可以很容易地通过每一个 阿特氏间期 取消除自身以外的所有任务的可调用文件。此操作当前在中完成 服务 为了简洁,但可以在 阿特氏间期 也。一个任务取消另一个任务将确保只能存在一个反馈。