asyncio-threading.md
1> **Источник:** https://python-all.ru/3.14/library/asyncio-threading.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# asyncio и свободно-поточный Python89asyncio использует цикл событий в качестве планировщика, чтобы обеспечить высокоэффективную параллельность за счёт переключения между задачами для неблокирующих операций ввода-вывода. Это даёт лучшую производительность для сценариев, связанных с вводом-выводом. Он также позволяет выгружать процессороёмкие задачи в пул потоков или процессов, но это по-прежнему ограничено [глобальной блокировкой интерпретатора](https://python-all.ru/3.14/glossary.html#term-global-interpreter-lock) в CPython.1011Однако в [свободно-поточном Python](https://python-all.ru/3.14/howto/free-threading-python.html#freethreading-python-howto), GIL отключён, и Python может выполнять настоящий многопоточный код. Это означает, что asyncio теперь может задействовать несколько ядер CPU без ограничений, накладываемых GIL.1213Начиная с Python 3.14, asyncio имеет первоклассную поддержку свободно-поточного Python, и реализация asyncio безопасна для использования в многопоточной среде.1415Один цикл событий на одном ядре может обрабатывать множество соединений параллельно, но код Python, выполняемый для обработки каждого из них, всё равно выполняется последовательно. Как только запросы требуют нетривиального объёма вычислений на один запрос, обработка становится узким местом, и одно ядро больше не справляется. Объединение asyncio с потоками наиболее полезно здесь: запуская цикл событий на поток, обработка разных запросов может выполняться параллельно на нескольких ядрах CPU. Это также полезно, когда нужно выполнить блокирующий или процессороёмкий код из приложения asyncio.1617> **См. также**18>19> [Масштабирование asyncio в свободно-поточном Python](https://python-all.ru/3.14/library/asyncio-threading.html), статья в блоге Кумара Адитьи, в которой объясняются внутренние изменения, делающие asyncio безопасным и эффективным в свободно-поточном Python, вместе с бенчмарками полученных улучшений.2021## Вопросы потокобезопасности2223Хотя asyncio разработан как потокобезопасный в среде свободно-поточного Python, всё же есть некоторые моменты, которые следует учитывать при использовании asyncio с потоками:24251. **Цикл событий**: Каждый поток должен иметь свой собственный цикл событий, который не должен использоваться совместно с другими потоками. Это гарантирует, что цикл событий может управлять своими задачами и колбэками без вмешательства со стороны других потоков.262. **Управление задачами**: Задачи и future, созданные в одном потоке, не должны ожидаться или изменяться из другого потока.273. **Потокобезопасные API**: При взаимодействии с asyncio из нескольких потоков важно использовать потокобезопасные API, предоставляемые asyncio, например [`asyncio.run_coroutine_threadsafe()`](https://python-all.ru/3.14/library/asyncio-task.html#asyncio.run_coroutine_threadsafe) для отправки корутин в цикл событий из другого потока. Если нужно вызвать колбэк из другого потока, можно использовать [`loop.call_soon_threadsafe()`](https://python-all.ru/3.14/library/asyncio-eventloop.html#asyncio.loop.call_soon_threadsafe) для безопасного планирования.284. **Синхронизация**: Примитивы синхронизации, предоставляемые asyncio (например, [`asyncio.Lock`](https://python-all.ru/3.14/library/asyncio-sync.html#asyncio.Lock) и [`asyncio.Event`](https://python-all.ru/3.14/library/asyncio-sync.html#asyncio.Event)), не предназначены для использования между потоками. Если нужно синхронизировать потоки, следует использовать примитивы синхронизации из модуля [`threading`](https://python-all.ru/3.14/library/threading.html#module-threading).2930## Использование asyncio с потоками3132asyncio поддерживает запуск одного цикла событий на поток, что позволяет задействовать несколько ядер CPU в среде свободно-поточного Python. Каждый поток может запускать свой собственный цикл событий, и задачи могут планироваться в этих циклах независимо.3334Вот пример использования asyncio с потоками:3536```python37import asyncio38import threading3940async def worker(name: str) -> None:41 print(f"Worker {name} starting")42 await asyncio.sleep(1)43 print(f"Worker {name} done")4445def run_loop(name: str) -> None:46 asyncio.run(worker(name))4748threads = [49 threading.Thread(target=run_loop, args=(f"T{i}",))50 for i in range(4)51]52for t in threads:53 t.start()54for t in threads:55 t.join()56```5758В этом примере каждый поток создаёт свой собственный цикл событий с помощью [`asyncio.run()`](https://python-all.ru/3.14/library/asyncio-runner.html#asyncio.run) и запускает на нём корутину. Потоки выполняются одновременно, и в сборке со свободными потоками они могут выполняться на разных ядрах CPU параллельно.5960## Производитель/потребитель между потоками6162Когда обычному (не asyncio) потоку нужно передать работу циклу событий asyncio, работающему в другом потоке, следует использовать потокобезопасный примитив, например [`queue.Queue`](https://python-all.ru/3.14/library/queue.html#queue.Queue), а не [`asyncio.Queue`](https://python-all.ru/3.14/library/asyncio-queue.html#asyncio.Queue), который безопасен только внутри одного цикла событий:6364```python65import asyncio66import queue67import threading6869def producer(q: queue.Queue[int]) -> None:70 for i in range(5):71 print(f"Producing {i}")72 q.put(i)73 q.shutdown()7475async def consumer(q: queue.Queue[int]) -> None:76 while True:77 try:78 item = q.get_nowait()79 except queue.Empty:80 await asyncio.sleep(0.1)81 continue82 except queue.ShutDown:83 break84 print(f"Consumed {item}")85 await asyncio.sleep(item)8687q: queue.Queue[int] = queue.Queue()88consumer_thread = threading.Thread(89 target=lambda: asyncio.run(consumer(q))90)91consumer_thread.start()92producer(q)93consumer_thread.join()94```9596Производитель выполняется в главном потоке, а потребитель – внутри цикла событий в своём собственном потоке, но они безопасно взаимодействуют через `queue.Queue`. Когда очередь пуста, потребитель ненадолго засыпает и повторяет попытку. Когда производитель завершает работу, он вызывает [`shutdown()`](https://python-all.ru/3.14/library/queue.html#queue.Queue.shutdown), что заставляет последующие вызовы [`get_nowait()`](https://python-all.ru/3.14/library/queue.html#queue.Queue.get_nowait) порождать [`queue.ShutDown`](https://python-all.ru/3.14/library/queue.html#queue.ShutDown), чтобы потребитель мог корректно завершиться.97