Документация Python неофициальный перевод

asyncio-dev.md

191 строк · 12.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.9/library/asyncio-dev.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Разработка с asyncio89Асинхронное программирование отличается от классического «последовательного» программирования.1011На этой странице перечислены типичные ошибки и ловушки, а также объясняется, как их избежать.1213## Режим отладки1415По умолчанию asyncio работает в производственном режиме. Для упрощения разработки в asyncio есть *режим отладки*.1617Включить режим отладки asyncio можно несколькими способами:1819- Установка переменной окружения [`PYTHONASYNCIODEBUG`](https://python-all.ru/3.9/using/cmdline.html#envvar-PYTHONASYNCIODEBUG) в значение `1`.20- Использование [режима разработки Python](https://python-all.ru/3.9/library/devmode.html#devmode).21- Передача `debug=True` в [`asyncio.run()`](https://python-all.ru/3.9/library/asyncio-task.html#asyncio.run).22- Вызов [`loop.set_debug()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.set_debug).2324В дополнение к включению режима отладки также рекомендуется:2526- Установка уровня журналирования [логгера asyncio](https://python-all.ru/3.9/library/asyncio-dev.html#asyncio-logger) на `logging.DEBUG`; например, следующий фрагмент кода можно выполнить при запуске приложения:2728  ```python29  logging.basicConfig(level=logging.DEBUG)30  ```31- Настройка модуля [`warnings`](https://python-all.ru/3.9/library/warnings.html#module-warnings) на отображение предупреждений [`ResourceWarning`](https://python-all.ru/3.9/library/exceptions.html#ResourceWarning). Один из способов сделать это – использовать параметр командной строки [`-W`](https://python-all.ru/3.9/using/cmdline.html#cmdoption-w) `default`.3233Когда режим отладки включён:3435- asyncio проверяет [корутины, которые не были ожиданы](https://python-all.ru/3.9/library/asyncio-dev.html#asyncio-coroutine-not-scheduled) и регистрирует их; это помогает избежать ошибки «забытого await».36- Многие небезопасные по отношению к потокам API asyncio (например, методы [`loop.call_soon()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.call_soon) и [`loop.call_at()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.call_at)) вызывают исключение, если их вызывают не из того потока.37- Время выполнения селектора ввода-вывода журналируется, если выполнение операции ввода-вывода занимает слишком много времени.38- Колбэки, выполняющиеся дольше 100 мс, записываются в журнал. С помощью атрибута `loop.slow_callback_duration` можно задать минимальную длительность выполнения в секундах, которая считается «медленной».3940## Параллелизм и многопоточность4142Цикл событий работает в потоке (обычно в главном) и выполняет все колбэки и задачи в своём потоке. Пока задача выполняется в цикле событий, никакие другие задачи не могут выполняться в том же потоке. Когда задача выполняет выражение `await`, она приостанавливается, и цикл событий переходит к следующей задаче.4344Для планирования [колбэка](https://python-all.ru/3.9/glossary.html#term-callback) из другого потока ОС следует использовать метод [`loop.call_soon_threadsafe()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.call_soon_threadsafe). Пример:4546```python47loop.call_soon_threadsafe(callback, *args)48```4950Почти все объекты asyncio не являются потокобезопасными, что обычно не является проблемой, если только нет кода, который работает с ними вне задачи или колбэка. Если такому коду нужно вызвать низкоуровневый API asyncio, следует использовать метод [`loop.call_soon_threadsafe()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.call_soon_threadsafe), например:5152```python53loop.call_soon_threadsafe(fut.cancel)54```5556Для планирования объекта корутины из другого потока ОС следует использовать функцию [`run_coroutine_threadsafe()`](https://python-all.ru/3.9/library/asyncio-task.html#asyncio.run_coroutine_threadsafe). Она возвращает [`concurrent.futures.Future`](https://python-all.ru/3.9/library/concurrent.futures.html#concurrent.futures.Future) для доступа к результату:5758```python59async def coro_func():60     return await asyncio.sleep(1, 42)6162# Позже в другом потоке ОС:6364future = asyncio.run_coroutine_threadsafe(coro_func(), loop)65# Ожидать результат:66result = future.result()67```6869Для обработки сигналов и выполнения подпроцессов цикл событий должен запускаться в главном потоке.7071Метод [`loop.run_in_executor()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) можно использовать с [`concurrent.futures.ThreadPoolExecutor`](https://python-all.ru/3.9/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor) для выполнения блокирующего кода в другом потоке ОС, не блокируя поток ОС, в котором работает цикл событий.7273В настоящее время нет способа напрямую планировать корутины или колбэки из другого процесса (например, запущенного с помощью [`multiprocessing`](https://python-all.ru/3.9/library/multiprocessing.html#module-multiprocessing)). Раздел [Методы цикла событий](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio-event-loop) перечисляет API, которые могут читать из каналов и отслеживать файловые дескрипторы, не блокируя цикл событий. Кроме того, API [подпроцессов](https://python-all.ru/3.9/library/asyncio-subprocess.html#asyncio-subprocess) asyncio предоставляют способ запустить процесс и взаимодействовать с ним из цикла событий. Наконец, упомянутый метод [`loop.run_in_executor()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) также может использоваться с [`concurrent.futures.ProcessPoolExecutor`](https://python-all.ru/3.9/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor) для выполнения кода в другом процессе.7475## Выполнение блокирующего кода7677Блокирующий (связанный с процессором) код не следует вызывать напрямую. Например, если функция выполняет интенсивные вычисления в течение 1 секунды, все параллельные задачи asyncio и операции ввода-вывода будут задержаны на 1 секунду.7879Исполнитель можно использовать для выполнения задачи в другом потоке или даже в другом процессе, чтобы избежать блокировки потока ОС циклом событий. Подробнее см. метод [`loop.run_in_executor()`](https://python-all.ru/3.9/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) для получения дополнительных сведений.8081## Журналирование8283asyncio использует модуль [`logging`](https://python-all.ru/3.9/library/logging.html#module-logging), а все логирование выполняется через логгер `"asyncio"`.8485Уровень логирования по умолчанию – `logging.INFO`. Его можно легко изменить:8687```python88logging.getLogger("asyncio").setLevel(logging.WARNING)89```9091## Обнаружение никогда не ожидаемых корутин9293Когда корутинная функция вызывается, но не ожидается (например, `coro()` вместо `await coro()`) или корутина не запланирована с помощью [`asyncio.create_task()`](https://python-all.ru/3.9/library/asyncio-task.html#asyncio.create_task), asyncio выдаёт сообщение [`RuntimeWarning`](https://python-all.ru/3.9/library/exceptions.html#RuntimeWarning):9495```python96import asyncio9798async def test():99    print("never scheduled")100101async def main():102    test()103104asyncio.run(main())105```106107Вывод:108109```python110test.py:7: RuntimeWarning: coroutine 'test' was never awaited111  test()112```113114Вывод в режиме отладки:115116```python117test.py:7: RuntimeWarning: coroutine 'test' was never awaited118Coroutine created at (most recent call last)119  File "../t.py", line 9, in <module>120    asyncio.run(main(), debug=True)121122  < .. >123124  File "../t.py", line 7, in main125    test()126  test()127```128129Обычно это исправляется либо ожиданием корутины, либо вызовом функции [`asyncio.create_task()`](https://python-all.ru/3.9/library/asyncio-task.html#asyncio.create_task):130131```python132async def main():133    await test()134```135136## Обнаружение никогда не извлечённых исключений137138Если вызван метод [`Future.set_exception()`](https://python-all.ru/3.9/library/asyncio-future.html#asyncio.Future.set_exception), но сам объект Future никогда не ожидается, исключение не будет передано пользовательскому коду. В этом случае asyncio выдаёт сообщение в журнал при сборке мусора для этого Future.139140Пример необработанного исключения:141142```python143import asyncio144145async def bug():146    raise Exception("not consumed")147148async def main():149    asyncio.create_task(bug())150151asyncio.run(main())152```153154Вывод:155156```python157Task exception was never retrieved158future: <Task finished coro=<bug() done, defined at test.py:3>159  exception=Exception('not consumed')>160161Traceback (most recent call last):162  File "test.py", line 4, in bug163    raise Exception("not consumed")164Exception: not consumed165```166167[Включите режим отладки](https://python-all.ru/3.9/library/asyncio-dev.html#asyncio-debug-mode), чтобы получить трассировку стека на момент создания задачи:168169```python170asyncio.run(main(), debug=True)171```172173Вывод в режиме отладки:174175```python176Task exception was never retrieved177future: <Task finished coro=<bug() done, defined at test.py:3>178    exception=Exception('not consumed') created at asyncio/tasks.py:321>179180source_traceback: Object created at (most recent call last):181  File "../t.py", line 9, in <module>182    asyncio.run(main(), debug=True)183184< .. >185186Traceback (most recent call last):187  File "../t.py", line 4, in bug188    raise Exception("not consumed")189Exception: not consumed190```191