Что такое конкурентность?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Конкурентность (Concurrency) в программировании
Конкурентность — это способность программы выполнять несколько задач логически независимо, чередуя их выполнение. Это НЕ то же самое, что параллелизм.
Конкурентность vs Параллелизм
Это часто путают, но это разные концепции:
КОНКУРЕНТНОСТЬ (Concurrency):
Один процессор, несколько задач, частое переключение
Задача A: [#### ]
Задача B: [### ]
Задача C: [####]
Выполнение: A -> B -> A -> C -> B -> C -> A...
Пример: одна горячая линия (один оператор), разговаривающий
с несколькими клиентами, переключаясь между ними.
ПАРАЛЛЕЛИЗМ (Parallelism):
Несколько процессоров, одновременное выполнение
Ядро 1: Задача A [########]
Ядро 2: Задача B [########]
Ядро 3: Задача C [########]
Все выполняются ОДНОВРЕМЕННО
Пример: три горячие линии, три оператора, каждый
разговаривает со своим клиентом одновременно.
Модели конкурентности
1. Асинхронность (Async/Await)
Задачи явно отпускают управление, позволяя другим выполняться:
import asyncio
async def fetch_user(user_id):
print(f"Fetching user {user_id}...")
await asyncio.sleep(2) # Имитируем сетевой запрос
print(f"User {user_id} fetched")
return f"User {user_id}"
async def main():
# Все три задачи начнут выполняться, но не одновременно
# Вместо этого они чередуются при await
results = await asyncio.gather(
fetch_user(1),
fetch_user(2),
fetch_user(3),
)
print(results)
# Выполнится примерно за 2 сек (не 6 сек)
asyncio.run(main())
# Порядок выполнения:
# Fetching user 1...
# Fetching user 2...
# Fetching user 3...
# (2 сек ожидания)
# User 1 fetched
# User 2 fetched
# User 3 fetched
2. Многопоточность (Threading)
ОС управляет переключением между потоками:
import threading
import time
def worker(name):
print(f"Worker {name} started")
time.sleep(2) # Блокирующая операция
print(f"Worker {name} finished")
threads = [
threading.Thread(target=worker, args=(1,)),
threading.Thread(target=worker, args=(2,)),
threading.Thread(target=worker, args=(3,)),
]
for t in threads:
t.start()
for t in threads:
t.join()
# Выполнится примерно за 2 сек (не 6 сек)
# ОС переключается между потоками
3. Многопроцессность (Multiprocessing)
Несколько процессов выполняются действительно параллельно:
from multiprocessing import Pool
import time
def cpu_task(n):
print(f"Process {n} started")
total = 0
for i in range(100_000_000):
total += i
print(f"Process {n} finished")
return total
with Pool(processes=3) as pool:
# На многоядерной машине выполнится действительно параллельно
results = pool.map(cpu_task, [1, 2, 3])
print(results)
Практические сценарии
Сценарий 1: Web скрейпинг (I/O-bound)
import asyncio
import aiohttp
async def scrape_page(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
urls = [https://example.com/page1, https://example.com/page2]
# Конкурентно загружаем страницы
results = await asyncio.gather(*[scrape_page(url) for url in urls])
return results
# Лучшее решение для I/O-bound
asyncio.run(main())
Сценарий 2: Обработка данных (CPU-bound)
from multiprocessing import Pool
def process_data(chunk):
# Дорогая обработка
return sum(chunk)
def main():
data = [list(range(i*1000, (i+1)*1000)) for i in range(10)]
# Используем многопроцессность для параллельного выполнения
with Pool(processes=4) as pool:
results = pool.map(process_data, data)
return results
Сценарий 3: Web сервер (смешанная нагрузка)
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# Конкурентно обработать несколько запросов
user = await fetch_from_db(user_id) # I/O
posts = await fetch_user_posts(user_id) # I/O
return {"user": user, "posts": posts}
# FastAPI автоматически управляет конкурентностью
# и может обработать 1000+ одновременных запросов
Проблемы конкурентности
Race Conditions (состояния гонки)
import threading
counter = 0
def increment():
global counter
for _ in range(1_000_000):
counter += 1 # ❌ Race condition!
threads = [threading.Thread(target=increment) for _ in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # Вместо 2_000_000 может быть < 2_000_000
Решение с Lock:
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1_000_000):
with lock: # ✅ Защищаем доступ
counter += 1
threads = [threading.Thread(target=increment) for _ in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # Всегда 2_000_000
Выбор модели
I/O-bound (сетевые запросы, файлы):
1. asyncio (лучше всего)
2. threading (проще)
CPU-bound (расчёты):
1. multiprocessing (обходит GIL)
2. asyncio + C extensions
Микс:
1. asyncio для I/O + thread pool для CPU
2. Или просто asyncio с async-friendly библиотеками
Итог
Конкурентность — это разделение времени выполнения между задачами:
- Asyncio — для I/O-bound задач, очень эффективно
- Threading — более простая альтернатива asyncio
- Multiprocessing — для параллельного CPU-bound выполнения
- Требует синхронизации — locks, queues, events, чтобы избежать race conditions
Правильный выбор модели может дать ускорение в 10-100 раз!