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

Что такое конкурентность?

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

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

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

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

Конкурентность (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 раз!