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

В чем разница между корутинами и потоками в Python?

2.0 Middle🔥 241 комментариев
#Python Core#Асинхронность и многопоточность

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

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

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

Разница между корутинами и потоками в Python

Корутины и потоки — это два разных подхода к конкурентному программированию в Python, имеющие кардинальные отличия в производительности, простоте использования и применении.

Потоки (Threading)

Потоки — это легковесные процессы, которые работают в контексте одного процесса, но выполняются параллельно на разных ядрах процессора.

import threading
import time

def worker(name):
    for i in range(3):
        print(f"{name}: итерация {i}")
        time.sleep(1)

# Создание потоков
thread1 = threading.Thread(target=worker, args=("Thread-1",))
thread2 = threading.Thread(target=worker, args=("Thread-2",))

thread1.start()
thread2.start()

thread1.join()  # Ожидание завершения
thread2.join()

Проблема GIL (Global Interpreter Lock): В CPython потоки не выполняются по-настоящему параллельно из-за GIL. Только один поток может выполнять Python код одновременно, поэтому потоки полезны только для I/O-bound задач.

Корутины (Async/Await)

Корутины — это функции, которые могут приостанавливаться и возобновляться, предоставляя асинхронное выполнение на одном потоке.

import asyncio

async def worker(name):
    for i in range(3):
        print(f"{name}: итерация {i}")
        await asyncio.sleep(1)  # Не блокирует поток

async def main():
    # Одновременное выполнение корутин
    await asyncio.gather(
        worker("Coro-1"),
        worker("Coro-2")
    )

asyncio.run(main())

Ключевые отличия

АспектПотокиКорутины
ПриродаИстинный параллелизм (ОС)Кооперативная конкурентность
GILОграничены GILНе затронуты GIL
ПереключениеВытесняющее (ОС решает)Кооперативное (код решает)
ИздержкиВысокие на создание/переключениеНизкие, легко создать 1000+
I/O операцииБлокируют потокНе блокируют, даёт ход другим
СинхронизацияLocks, Conditions (сложно)asyncio API (просто)
МасштабируемостьДо 100-200 потоковДо 100,000+ корутин

Практический пример: I/O-bound задача

С потоками (неэффективно):

import threading
import requests
import time

def fetch_url(url):
    response = requests.get(url)
    print(f"Получен ответ: {response.status_code}")
    return response

start = time.time()
threads = []
for url in urls:
    t = threading.Thread(target=fetch_url, args=(url,))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print(f"Потоки: {time.time() - start}s")

С корутинами (эффективно):

import asyncio
import aiohttp
import time

async def fetch_url(session, url):
    async with session.get(url) as response:
        print(f"Получен ответ: {response.status}")
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    return results

start = time.time()
results = asyncio.run(main())
print(f"Корутины: {time.time() - start}s")

Когда использовать

Потоки:

  • CPU-bound задачи (вычисления на многоядерных системах)
  • Нужен истинный параллелизм
  • Интеграция с блокирующими библиотеками
  • Простые задачи с малым количеством потоков

Корутины:

  • I/O-bound задачи (сетевые запросы, чтение файлов)
  • Нужно обработать много одновременных операций
  • Web-приложения и серверы (aiohttp, FastAPI)
  • Масштабируемость и производительность

Гибридный подход

import asyncio
from concurrent.futures import ThreadPoolExecutor

# CPU-bound функция в отдельном потоке
def cpu_intensive(n):
    return sum(i * i for i in range(n))

async def main():
    loop = asyncio.get_event_loop()
    executor = ThreadPoolExecutor()
    
    # Выполнение CPU-bound в потоке
    result = await loop.run_in_executor(executor, cpu_intensive, 1000000)
    print(f"Результат: {result}")

asyncio.run(main())

Заключение

Потоки подходят для истинного параллелизма и CPU-bound задач, но ограничены GIL и издержками на переключение контекста. Корутины идеальны для I/O-bound задач с высокой конкурентностью и масштабируемостью. Выбор зависит от типа задачи и требований к производительности.