Какие знаешь виды конкурентного программирования в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь виды конкурентного программирования в Python?
В Python есть три основных парадигмы для конкурентного программирования, каждая с собственными преимуществами и ограничениями. Рассмотрим их по глубине.
1. Threading (многопоточность)
Многопоточность использует несколько потоков, работающих в рамках одного процесса. Все потоки делят одно адресное пространство.
import threading
import time
def worker(worker_id):
"""Функция, которая будет работать в отдельном потоке"""
for i in range(3):
print(f"Worker {worker_id}: iteration {i}")
time.sleep(0.1) # Имитация работы
print(f"Worker {worker_id}: done")
# Создание и запуск потоков
threads = []
for i in range(3):
thread = threading.Thread(target=worker, args=(i,))
thread.start()
threads.append(thread)
# Ожидание завершения всех потоков
for thread in threads:
thread.join()
print("All threads completed")
Преимущества:
- Простая синтаксис и понимание
- Потоки легко создавать
- Хорошо для I/O-bound операций (сетевые запросы, файловые операции)
Недостатки:
- GIL (Global Interpreter Lock) — только один поток может выполнять Python код одновременно
- Непригодна для CPU-bound операций
- Сложна для отладки (race conditions, deadlocks)
- Требует синхронизации (Lock, RLock, Semaphore)
import threading
# Проблема: race condition
counter = 0
lock = threading.Lock()
def increment():
global counter
with lock: # Необходима блокировка для безопасности
temp = counter
temp += 1
counter = temp
# Без lock возможна потеря обновлений!
2. Multiprocessing (многопроцессность)
Многопроцессность создает отдельные процессы с независимыми интерпретаторами Python. Каждый процесс имеет собственный GIL.
import multiprocessing
import time
def worker(worker_id):
"""Функция, выполняемая в отдельном процессе"""
for i in range(3):
print(f"Worker {worker_id} (PID {multiprocessing.current_process().pid}): iteration {i}")
time.sleep(0.1)
print(f"Worker {worker_id}: done")
if __name__ == "__main__":
processes = []
for i in range(3):
process = multiprocessing.Process(target=worker, args=(i,))
process.start()
processes.append(process)
# Ожидание завершения всех процессов
for process in processes:
process.join()
print("All processes completed")
# Output:
# Worker 0 (PID 12345): iteration 0
# Worker 1 (PID 12346): iteration 0
# Worker 2 (PID 12347): iteration 0
# ...
Преимущества:
- Избегает GIL — каждый процесс имеет собственный интерпретатор
- Отлично для CPU-bound операций (математические вычисления, обработка данных)
- Процессы полностью изолированы
Недостатки:
- Дорого запускать (высокие затраты на память и инициализацию)
- Обмен данными требует сериализации (pickle)
- Более сложная синхронизация (Queue, Pipe)
import multiprocessing
def expensive_calculation(n):
"""CPU-bound операция"""
return sum(i*i for i in range(n))
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
# Параллельная обработка с 4 процессами
results = pool.map(expensive_calculation, [1000000, 2000000, 3000000, 4000000])
print(results)
3. Asyncio (асинхронное программирование)
Асинхронное программирование использует один поток, но обрабатывает множество задач асинхронно (cooperative multitasking). Когда одна задача ждет I/O, другая может выполняться.
import asyncio
async def worker(worker_id):
"""Асинхронная функция (корутина)"""
for i in range(3):
print(f"Worker {worker_id}: iteration {i}")
await asyncio.sleep(0.1) # await позволяет другим корутинам работать
print(f"Worker {worker_id}: done")
async def main():
# Создание и запуск 3 задач параллельно
tasks = [worker(i) for i in range(3)]
await asyncio.gather(*tasks)
print("All tasks completed")
# Запуск async программы
asyncio.run(main())
# Output:
# Worker 0: iteration 0
# Worker 1: iteration 0
# Worker 2: iteration 0
# Worker 0: iteration 1
# ...
Преимущества:
- Очень быстро и эффективно для I/O-bound операций
- Один поток → нет race conditions
- Меньше памяти, чем threading/multiprocessing
- Отлично для high-concurrency (тысячи одновременных соединений)
Недостатки:
- GIL все еще ограничивает CPU-bound операции
- Требует async/await синтаксиса (не совместимо с обычным кодом)
- Сложнее для начинающих
import asyncio
import aiohttp
async def fetch_url(session, url):
"""Асинхронный HTTP запрос"""
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://example.com/1",
"https://example.com/2",
"https://example.com/3",
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks) # Все запросы параллельно!
return results
# 3 сетевых запроса выполняются параллельно (не последовательно)
# Total time: ~1 second (вместо 3 секунд при последовательном выполнении)
Сравнительная таблица
| Свойство | Threading | Multiprocessing | Asyncio |
|---|---|---|---|
| Использование памяти | Среднее | Высокое | Низкое |
| CPU-bound | Нет (GIL) | Да | Нет (GIL) |
| I/O-bound | Да | Да | Да (лучше всех) |
| Простота | Простая | Средняя | Средняя |
| Race conditions | Риск | Низкий | Нет |
| Масштабируемость | До ~100 потоков | До ~1000 процессов | До ~10,000+ задач |
| Синхронизация | Lock, Condition | Queue, Pipe | Event, Lock |
Практические примеры для выбора
Используй Threading для:
- Веб-скрейпинг с небольшим количеством параллельных запросов
- Работа с файловой системой
- Создание простого многопоточного сервера
Используй Multiprocessing для:
- Обработка больших массивов данных (machine learning)
- Математические вычисления
- Независимые задачи, которые не требуют частого взаимодействия
Используй Asyncio для:
- Web фреймворки (FastAPI, aiohttp)
- Микросервисы с множеством I/O операций
- Чатботы (Telegram, Discord)
- Real-time приложения (WebSockets)
Гибридный подход
import asyncio
import multiprocessing
from concurrent.futures import ProcessPoolExecutor
async def cpu_bound_task(n):
"""CPU-bound операция в отдельном процессе"""
def calculate():
return sum(i*i for i in range(n))
loop = asyncio.get_event_loop()
with ProcessPoolExecutor() as executor:
result = await loop.run_in_executor(executor, calculate)
return result
async def main():
# Комбинируем asyncio для I/O и multiprocessing для CPU
io_task = asyncio.sleep(1)
cpu_task = cpu_bound_task(10000000)
results = await asyncio.gather(io_task, cpu_task)
print(results)
asyncio.run(main())
Python 3.13+ : asyncio improvements
В новых версиях Python asyncio получил серьезные оптимизации и теперь может конкурировать по производительности с Go и Rust для I/O-bound операций.