我的协程太大了,我想将其拆分以提高可读性。
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
对其进行调用,所以它的执行可能会延迟。
如何确保协程立即运行并且不会延迟?
答案 0 :(得分:2)
简而言之:使用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)
来获得事件循环“未卡住”。但是,在生产异步代码中,绝对不需要这种事情,在该代码中,应该对程序进行结构化,以使每个协程执行相对较少的工作,然后使用await
获得更多数据。