Сколько потоков и процессов используется в asyncio?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Количество потоков и процессов в asyncio
Ответ: Ни один процесс, один поток
asyncio использует один поток выполнения и ни одного дополнительного процесса по умолчанию. Это критически важно понимать для правильного использования асинхронного программирования в Python.
Как это работает
asyncio работает на базе event loop — встроенного механизма планирования задач, который работает исключительно в одном потоке:
import asyncio
async def task1():
print("Задача 1 начало")
await asyncio.sleep(1)
print("Задача 1 конец")
async def task2():
print("Задача 2 начало")
await asyncio.sleep(1)
print("Задача 2 конец")
async def main():
# Обе задачи работают в ОДНОМ потоке
await asyncio.gather(task1(), task2())
asyncio.run(main())
Вывод:
Задача 1 начало
Задача 2 начало
Задача 1 конец
Задача 2 конец
Обе задачи выполняются в одном потоке, а не параллельно!
Почему это однопоточно?
Event loop — это цикл, который:
- Выбирает одну задачу из очереди
- Выполняет её до первого
await - Приостанавливает задачу
- Переходит к следующей задаче
Это называется concurrency (параллелизм), а не parallelism (истинный параллелизм).
import time
async def slow_io():
print(f"Начало, поток: {id(asyncio.current_task())}")
await asyncio.sleep(2) # Даёт управление event loop
print(f"Конец, поток: {id(asyncio.current_task())}")
async def main():
# Оба выполнятся в одном потоке
await asyncio.gather(slow_io(), slow_io())
start = time.time()
asyncio.run(main())
print(f"Время: {time.time() - start}")
# Результат: ~2 сек (не 4!), потому что задачи чередуются
Если нужна реальная параллельность
Для CPU-bound операций нужны процессы или потоки. asyncio может работать с ними:
import asyncio
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
def cpu_bound_task(n):
# CPU-bound операция
total = 0
for i in range(n):
total += i
return total
async def main():
# Используем процессы для CPU-bound работы
loop = asyncio.get_event_loop()
with ProcessPoolExecutor(max_workers=4) as executor:
result = await loop.run_in_executor(executor, cpu_bound_task, 10000000)
print(result)
asyncio.run(main())
Это создаст 4 процесса дополнительно к основному потоку.
Потоки в asyncio
Esли понадобятся потоки, asyncio может их использовать:
import asyncio
from concurrent.futures import ThreadPoolExecutor
import threading
def blocking_io():
print(f"Поток: {threading.current_thread().name}")
# Блокирующая IO операция
import time
time.sleep(1)
return "Готово"
async def main():
loop = asyncio.get_event_loop()
# Используем пул потоков
with ThreadPoolExecutor(max_workers=2) as executor:
result = await loop.run_in_executor(executor, blocking_io)
print(result)
asyncio.run(main())
Таблица сравнения
| Подход | Потоки | Процессы | Для чего |
|---|---|---|---|
| asyncio (базовый) | 1 | 0 | IO-bound операции |
| asyncio + ThreadPoolExecutor | 1+ | 0 | Блокирующие IO |
| asyncio + ProcessPoolExecutor | 1 | 1+ | CPU-bound операции |
| threading | Много | 0 | Легкая многопоточность |
| multiprocessing | 1+ | Много | Истинный параллелизм |
Частая ошибка
# НЕПРАВИЛЬНО!
async def bad_example():
result = requests.get('https://example.com') # Блокирует весь event loop!
return result
# ПРАВИЛЬНО!
async def good_example():
result = await httpx.AsyncClient().get('https://example.com')
return result
Использование блокирующих библиотек в asyncio убивает все преимущества асинхронности.
Итог
asyncio — это многозадачность в одном потоке благодаря event loop. Если нужна реальная параллельность, нужны дополнительные процессы или потоки через run_in_executor.