У меня есть асинхронное приложение, которое я запускаю, и оно очень чувствительно ко времени, поэтому я пытаюсь проработать и убрать любой блокирующий код, который я могу найти.
Я использую asyncio.run()
с отладкой = True.
Как вы можете видеть, селектор ввода-вывода блокируется на 0,852 секунды при выходе из спящего режима, согласно документации Python:
The execution time of the I/O selector is logged if it takes too long to perform an I/O operation.
20/04/23 12:04:20 WARNING asyncio: Executing <Task pending name='Task-99' coro=<CodeRunner.run_forever() running at /code/testcode.py:929> wait_for=<Future pending cb=[Task.task_wakeup()] base_events.py:1917
created at /Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py:427> cb=[_run_until_complete_cb() at
/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py:180] created at
/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py:100> took 0.852 seconds
Рассматриваемый код:
while True:
if counter % CONTACT_SCHEDULE == 0:
await self.run_once()
counter = 0
log.info(f"Sleeping {CONTACT_SCHEDULE} seconds...")
progress.refresh()
counter += 1
await asyncio.sleep(1) <--- this is line 929
Мой вопрос: почему asyncio.sleep(1) вызывает зависание моего кода? Это потому, что что-то еще работает в то же время и блокирует?
Я думаю, что ваш вызов asyncio.sleep(1)
использует здесь цикл событий по умолчанию, который создает проблему, с которой вы столкнулись.
Вместо этого передайте свой цикл событий в качестве параметра в качестве параметра asyncio
await asyncio.sleep(1, loop=referenceToYourEventLoop)
Параметр loop
был удален в версии 3.10.
Это не сообщение о медленном селекторе ввода-вывода, и оно не говорит о том, что asyncio.sleep
является проблемой. Это медленное сообщение обратного вызова, описание которого вы увидите в следующем пункте маркированного списка в режиме отладки asyncio docs после части, которую вы процитировали:
- Обратные вызовы, занимающие более 100 мс, регистрируются. Атрибут loop.slow_callback_duration можно использовать для установки минимальной продолжительности выполнения в секундах, которая считается «медленной».
Судя по деталям сообщения, это похоже на внутренний обратный вызов, используемый для Task.__step, который запускает сопрограмму, в которую заворачивается задача, до тех пор, пока сопрограмма не приостановится.
asyncio.sleep
это не проблема. Это как раз то место, где ваша сопрограмма приостановилась. Предупреждающее сообщение пытается сообщить вам, что ваша сопрограмма работала непрерывно в течение 0,852 секунды между тем, когда она проснулась, и тем, когда она была приостановлена на asyncio.sleep
.
Мы не можем сказать, почему ваша сопрограмма заняла так много времени. Похоже, что-то очень медленное и синхронное произошло в self.run_once
или progress.refresh
, но у нас недостаточно информации, чтобы что-то сказать о том, что это было.
Большое спасибо @user2357112 - ценю понимание
Это не слишком длинное сообщение селектора ввода/вывода. Это медленное сообщение обратного вызова — обратный вызов, запланированный с помощью чего-то вроде
loop.call_soon
илиloop.call_soon_threadsafe
, занял больше 100 миллисекунд.