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

Насколько подходит многопоточность (multithreading) в Python для параллельных задач

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

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

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

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

Многопоточность в Python для параллельных задач

Вопрос о многопоточности в Python — один из самых обсуждаемых в сообществе. Ответ зависит от типа задач, которые вы решаете.

Ограничение: Global Interpreter Lock (GIL)

В CPython (стандартной реализации) существует так называемый Global Interpreter Lock — механизм, который позволяет только одному потоку выполнять Python bytecode одновременно. Это означает, что многопоточность не даёт паралелизма для CPU-bound задач:

import threading
import time

def cpu_bound_task():
    total = 0
    for i in range(100_000_000):
        total += i
    return total

# Однопоточный вариант
start = time.time()
cpu_bound_task()
print(f"Single-threaded: {time.time() - start:.2f}s")  # ~4-5 сек

# Многопоточный вариант
start = time.time()
threads = [
    threading.Thread(target=cpu_bound_task),
    threading.Thread(target=cpu_bound_task),
]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(f"Multi-threaded: {time.time() - start:.2f}s")  # всё ещё ~4-5 сек!

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

Когда многопоточность ПОЛЕЗНА

Многопоточность отлично подходит для I/O-bound задач — когда программа ждёт ответа от сети, диска, базы данных:

import threading
import requests
from time import time

def fetch_url(url):
    response = requests.get(url)
    return len(response.content)

urls = [https://example.com] * 10

# Последовательный вариант
start = time.time()
for url in urls:
    fetch_url(url)
print(f"Sequential: {time.time() - start:.2f}s")  # ~20 сек (2 сек на запрос)

# Многопоточный вариант
start = time.time()
threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(f"Multi-threaded: {time.time() - start:.2f}s")  # ~2 сек!

Здесь многопоточность даёт огромный выигрыш, потому что в момент ожидания сетевого ответа поток отпускает GIL, позволяя другим потокам работать.

Альтернативы

Для CPU-bound задач нужно использовать:

  1. multiprocessing — отдельные процессы Python, каждый со своим интерпретатором и GIL:
from multiprocessing import Pool

with Pool(processes=2) as pool:
    result = pool.map(cpu_bound_task, range(2))
  1. Асинхронность (asyncio) — для I/O-bound:
import asyncio
import aiohttp

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

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

asyncio.run(main())  # Более эффективно чем threads
  1. asyncio vs threads для I/O: asyncio лучше для тысяч одновременных соединений, threads достаточно для 10-100.

Итог

  • I/O-bound задачи: многопоточность отлично подходит ✅
  • CPU-bound задачи: используй multiprocessing или Cython ❌
  • Тысячи I/O: используй asyncio вместо threads ⚡

Многопоточность в Python — не универсальное решение, но мощный инструмент для блокирующих операций ввода-вывода.