← Назад к вопросам

Сколько потоков и процессов используется в asyncio?

3.0 Senior🔥 291 комментариев
#Архитектура и паттерны#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Количество потоков и процессов в 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 — это цикл, который:

  1. Выбирает одну задачу из очереди
  2. Выполняет её до первого await
  3. Приостанавливает задачу
  4. Переходит к следующей задаче

Это называется 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 (базовый)10IO-bound операции
asyncio + ThreadPoolExecutor1+0Блокирующие IO
asyncio + ProcessPoolExecutor11+CPU-bound операции
threadingМного0Легкая многопоточность
multiprocessing1+МногоИстинный параллелизм

Частая ошибка

# НЕПРАВИЛЬНО!
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.