【问题标题】:How to split a long coroutine without using await?如何在不使用等待的情况下拆分长协程?
【发布时间】:2018-06-30 15:02:09
【问题描述】:

我有一个太大的协程,为了便于阅读,我想拆分它。

async def handle_message(self, message):
    message_type = message.get('type')

    if message_type == 'broadcast':
        ...
        for n in self._neighbors:
            await self.send_message(n, message)

    elif message_type == 'graph':
        ...

我想将处理广播消息的部分提取到这样的私有方法中:

async def handle_message(self, message):
    message_type = message.get('type')
    ...

    if message_type = 'broadcast':
        await self._handle_broadcast(message)
    elif message_type = 'graph':
        ...

问题在于这会改变代码的行为,因为_handle_broadcast 部分是一个协程,并且它的执行可能会延迟,因为我用await 调用它。

有什么办法保证协程立即运行不延迟?

【问题讨论】:

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


    【解决方案1】:

    简而言之:使用 await 完全按照您开始的方式拆分协程。

    问题在于这会改变代码的行为,因为_handle_broadcast 部分是一个协程,并且它的执行可能会延迟,因为我使用await 调用它。

    无论好坏,这个前提都是错误的。当给定一个协程时,await 立即开始执行它,没有中间延迟。只有 如果 协程调用了导致它挂起的东西(例如 asyncio.sleep 或还没有数据的网络读取),您的协程才会随之挂起 - 这正是如果代码保持内联,你会得到什么。

    从这个意义上说,await <some coroutine> 的工作方式类似于常规函数调用的协程等价物,从而精确地允许您进行所需的非语义更改重构。这可以用一个例子来证明:

    import asyncio
    
    async def heartbeat():
        while True:
            print('tick')
            await asyncio.sleep(1)
    
    async def noop():
        pass
    
    async def coro():
        # despite "await", this blocks the event loop!
        while True:
            await noop()
    
    loop = asyncio.get_event_loop()
    loop.create_task(heartbeat())
    loop.create_task(coro())
    loop.run_forever()
    

    上面的代码阻塞了事件循环——尽管coro 在循环中除了await 什么都不做。所以await 并不能保证屈服于事件循环,协程必须通过其他方式来做到这一点。 (此行为也可能是bugs 的来源。)

    在上述情况下,可以通过插入await asyncio.sleep(0) 来“解除阻塞”事件循环。但是在生产 asyncio 代码中永远不需要这种东西,程序的结构应该使每个协程做的工作相对较少,然后使用await 获取更多数据。

    【讨论】:

    • 哇,所以我对await 的工作方式有误解。这个和您提供的链接实际上解释了很多关于我之前在同一个项目中遇到的问题,该项目也使用asyncio.open_connection。谢谢!
    • @JacqueGoupil 要进一步了解await 和事件循环的工作原理,您还可以查看this video。它解释了大多数 Python 程序员已经熟悉的生成器,但这也恰好是 await 在幕后工作的方式。
    • 我应该几个月前就看过这个视频了。
    猜你喜欢
    • 2020-04-19
    • 2012-11-16
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    相关资源
    最近更新 更多