Ответ
GIL (Global Interpreter Lock) — это мьютекс в CPython, который не позволяет нескольким нативным потокам выполнять Python байт-код одновременно в рамках одного процесса. Из-за этого стандартный модуль threading не обеспечивает параллелизма для CPU-bound задач.
Выбор правильного подхода зависит от типа задачи.
1. Для CPU-bound задач: multiprocessing
Этот модуль обходит GIL, создавая отдельные процессы операционной системы. Каждый процесс имеет свой собственный интерпретатор Python и, соответственно, свой собственный GIL. Это единственный способ добиться истинного параллелизма для вычислений на нескольких ядрах CPU.
Пример:
from multiprocessing import Pool
def square(x):
# Это CPU-bound задача
return x * x
if __name__ == '__main__':
# Запускаем 4 рабочих процесса
with Pool(4) as p:
results = p.map(square, [1, 2, 3, 4, 5, 6, 7, 8])
print(results) # Вывод: [1, 4, 9, 16, 25, 36, 49, 64]
2. Для I/O-bound задач: threading или asyncio
В задачах, ограниченных вводом-выводом (I/O), программа большую часть времени ждет ответа от сети, диска или базы данных. В эти моменты GIL освобождается, позволяя другим потокам выполняться.
-
threading: Использует вытесняющую многозадачность на уровне ОС. Подходит для интеграции с блокирующим кодом.import threading import requests def fetch_url(url): # GIL освобождается во время ожидания ответа от сети response = requests.get(url) print(f"Fetched {url}, status: {response.status_code}") urls = ["https://google.com", "https://bing.com"] threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls] for t in threads: t.start() for t in threads: t.join() -
asyncio: Использует кооперативную многозадачность в одном потоке. Более эффективно по памяти и производительности для тысяч одновременных I/O операций.import asyncio import aiohttp async def fetch_url(session, url): async with session.get(url) as response: print(f"Fetched {url}, status: {response.status}") async def main(): urls = ["https://google.com", "https://bing.com"] async with aiohttp.ClientSession() as session: tasks = [fetch_url(session, url) for url in urls] await asyncio.gather(*tasks) asyncio.run(main())
Итог:
- CPU-bound: Используйте
multiprocessing. - I/O-bound: Используйте
threadingдля простоты илиasyncioдля максимальной производительности.