У меня есть сопроцессор, который становится слишком большим, и я хотел бы разделить его на читаемость.
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
.
Каким образом гарантировать, что сопрограмма выполняется немедленно и не задерживается?
Короче: разделите coroutine точно так же, как вы начали, используя await
.
Проблема в том, что это изменяет поведение кода, поскольку часть
_handle_broadcast
является сопрограммой, и ее выполнение может задерживаться, так как я вызываю ее сawait
.
К лучшему или худшему, эта предпосылка ложна. При получении сопрограммы, await
сразу же начинает выполнение ее без промежуточной задержки. Это только, если эта сопрограмма вызывает что-то, что заставляет ее приостанавливать (например, asyncio.sleep
или чтение в сети, которое еще не имеет данных), что ваша сопрограмма приостанавливается вместе с ней - это именно то, что вы получили бы, если бы код остался в очереди.
В этом смысле await <some coroutine>
работает как эквивалент 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
не является гарантией уступки циклу событий, сопрограмма должна делать это с помощью других средств. (Это поведение также может быть источником ошибок.)
В приведенном выше случае можно получить цикл событий " await asyncio.sleep(0)
", вставив await asyncio.sleep(0)
. Но такого рода вещи никогда не должны понадобиться в производственном коде asyncio, где программа должна быть структурирована так, чтобы каждая сопрограмма выполняла сравнительно небольшую работу, а затем использует await
для получения большего количества данных.
await
. Это и ссылка, которую вы предоставили, на самом деле многое объясняют о проблемах, с которыми яasyncio.open_connection
в этом же проекте ранее, который также используетasyncio.open_connection
. Спасибо!await
и цикл обработки событий, вы также можете посмотреть это видео . Это объясняет вещи с точки зрения генераторов, с которыми большинство программистов на Python уже знакомы, но это также происходит именно так, какawait
работает за кулисами.