В чем разница между вытесняющей и конкурентной многозадачность?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Вытесняющая vs Конкурентная многозадачность
Это два способа организации выполнения нескольких задач "одновременно", но они работают принципиально по-разному.
Конкурентная многозадачность (Cooperative multitasking)
Задачи сами передают управление друг другу в нужные моменты.
import asyncio
async def task1():
print("Task 1: начало")
await asyncio.sleep(1) # Явно передаём управление
print("Task 1: конец")
async def task2():
print("Task 2: начало")
await asyncio.sleep(0.5)
print("Task 2: конец")
async def main():
await asyncio.gather(task1(), task2())
# Результат:
# Task 1: начало
# Task 2: начало
# Task 2: конец
# Task 1: конец
Здесь task1 говорит: "я жду 1 секунду, пока я жду, выполняй task2". После этого передача управления происходит явно через await.
Вытесняющая многозадачность (Preemptive multitasking)
Операционная система сама решает, какой процесс/поток выполнять в каждый момент времени. Может прервать в любой точке.
import threading
import time
def task1():
print("Task 1: начало")
time.sleep(1)
print("Task 1: конец")
def task2():
print("Task 2: начало")
time.sleep(0.5)
print("Task 2: конец")
# Создаём потоки
t1 = threading.Thread(target=task1)
t2 = threading.Thread(target=task2)
t1.start()
t2.start()
t1.join()
t2.join()
# Результат может быть:
# Task 1: начало
# Task 2: начало
# Task 2: конец
# Task 1: конец
ОС сама решает, когда дать процессорное время каждому потоку. Это может произойти в любой момент.
Ключевые различия
| Аспект | Конкурентная | Вытесняющая |
|---|---|---|
| Передача управления | Явная (await, yield) | Автоматическая (ОС) |
| Предсказуемость | Высокая (контролируем) | Низкая (может прервать) |
| Синхронизация | Проще (no race conditions) | Сложнее (нужны Lock, Semaphore) |
| Потребление ресурсов | Дешевле | Дороже |
| Масштабируемость | 10000+ задач | Обычно 100-1000 потоков |
| Context switch | Явный (когда await) | Частый (каждые ms) |
В Python
Конкурентная — asyncio:
import asyncio
async def fetch_data(url):
print(f"Fetching {url}")
await asyncio.sleep(1) # Имитация I/O
return f"Data from {url}"
async def main():
# Все запросы выполнятся "одновременно"
results = await asyncio.gather(
fetch_data("/api/users"),
fetch_data("/api/posts"),
fetch_data("/api/comments")
)
return results
asyncio.run(main())
Этот код выполнится примерно за 1 секунду, потому что все три fetch_data будут чередоваться.
Вытесняющая — threading:
import threading
from concurrent.futures import ThreadPoolExecutor
def fetch_data(url):
import time
print(f"Fetching {url}")
time.sleep(1)
return f"Data from {url}"
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(fetch_data, [
"/api/users",
"/api/posts",
"/api/comments"
]))
Тоже выполнится за ~1 секунду, но ОС управляет потоками.
Проблемы
Конкурентная (asyncio):
- Если забыть
await, код выполнится синхронно - Блокирующие операции остановят весь event loop
# НЕПРАВИЛЬНО - весь loop замерзнет
async def bad():
time.sleep(10) # Блокирующая операция!
await asyncio.sleep(1)
# ПРАВИЛЬНО
async def good():
await asyncio.sleep(10) # Неблокирующая
Вытесняющая (threading):
- Race conditions (два потока изменяют одну переменную)
- Deadlocks
- Нужны Lock для синхронизации
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
with lock: # Защищаем критическую секцию
counter += 1
threads = [threading.Thread(target=increment) for _ in range(100)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # Точно 100
Когда использовать
Asyncio (конкурентная):
- I/O-bound операции (HTTP запросы, БД запросы)
- Нужно обработать много соединений
- Простота синхронизации
Threading (вытесняющая):
- CPU-bound операции
- Когда нужны настоящие параллельные вычисления
- Интеграция с блокирующими библиотеками
Вывод: в Python asyncio предпочтительнее для web-приложений, т.к. большинство операций — I/O.