Ответ
В Python существует несколько механизмов для реализации конкурентного программирования, каждый из которых подходит для разных типов задач:
-
Потоки (Threading): Используются для I/O-bound задач, где основное время тратится на ожидание ввода/вывода (например, сетевые запросы, чтение/запись файлов). Потоки в Python не обеспечивают истинного параллелизма из-за Global Interpreter Lock (GIL), который позволяет выполнять только один поток байт-кода Python за раз. Однако, когда поток ожидает I/O, GIL освобождается, позволяя другим потокам выполняться.
import threading import time def io_task(name): print(f"Поток {name}: Начинаю I/O операцию...") time.sleep(1) # Имитация I/O ожидания print(f"Поток {name}: I/O операция завершена.") thread1 = threading.Thread(target=io_task, args=("1",)) thread2 = threading.Thread(target=io_task, args=("2",)) thread1.start() thread2.start() thread1.join() thread2.join() print("Все потоки завершены.") -
Процессы (Multiprocessing): Используются для CPU-bound задач, требующих интенсивных вычислений. Каждый процесс имеет свой собственный интерпретатор Python и, следовательно, свой собственный GIL. Это позволяет процессам выполняться параллельно на разных ядрах CPU, обходя ограничение GIL.
from multiprocessing import Process import os def cpu_task(name): print(f"Процесс {name} (PID: {os.getpid()}): Начинаю CPU-интенсивную операцию...") _ = sum(i*i for i in range(10**7)) # Имитация вычислений print(f"Процесс {name}: CPU-интенсивная операция завершена.") process1 = Process(target=cpu_task, args=("A",)) process2 = Process(target=cpu_task, args=("B",)) process1.start() process2.start() process1.join() process2.join() print("Все процессы завершены.") -
Асинхронность (Asyncio): Оптимально для высококонкурентных I/O-bound задач, где требуется управлять большим количеством одновременных операций ввода/вывода (например, веб-серверы, работа с базами данных).
asyncioиспользует один поток и один цикл событий (event loop) для кооперативной многозадачности. Корутины (функции, определённые сasync def) добровольно передают управление циклу событий, когда сталкиваются с ожиданием I/O (await), позволяя другим корутинам выполняться.import asyncio import time async def async_io_task(name): print(f"Корутина {name}: Начинаю асинхронную I/O операцию...") await asyncio.sleep(1) # Имитация асинхронного I/O ожидания print(f"Корутина {name}: Асинхронная I/O операция завершена.") async def main(): await asyncio.gather( async_io_task("X"), async_io_task("Y") ) asyncio.run(main()) print("Все асинхронные задачи завершены.") -
concurrent.futures: Это высокоуровневый модуль, предоставляющий абстракцию для запуска задач в пулах потоков (ThreadPoolExecutor) или процессов (ProcessPoolExecutor). Он упрощает управление конкурентными задачами, позволяя отправлять функции для выполнения и получать их результаты через объектыFuture.from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor import time def simple_task(name): time.sleep(0.5) return f"Задача {name} выполнена." # Пример с ThreadPoolExecutor (для I/O-bound) with ThreadPoolExecutor(max_workers=2) as executor: future1 = executor.submit(simple_task, "A") future2 = executor.submit(simple_task, "B") print(future1.result()) print(future2.result()) # Пример с ProcessPoolExecutor (для CPU-bound) # with ProcessPoolExecutor(max_workers=2) as executor: # future1 = executor.submit(simple_task, "C") # future2 = executor.submit(simple_task, "D") # print(future1.result()) # print(future2.result())
Выбор подходящего механизма зависит от характера задачи: threading для I/O-bound с небольшим количеством потоков, multiprocessing для CPU-bound, asyncio для высококонкурентных I/O-bound задач, и concurrent.futures как удобная обёртка для обоих.