Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь виды многозадачности?
Многозадачность — это способность системы выполнять несколько задач одновременно или квазиодновременно. В Python существует несколько фундаментально разных типов.
1. Многопроцессность (Multiprocessing) — истинный параллелизм
Это настоящий параллелизм — разные процессы работают на разных ядрах процессора:
from multiprocessing import Process, cpu_count
import time
def cpu_intensive_task(n):
"""Задача, требующая вычислений"""
result = 0
for i in range(n):
result += i ** 2
return result
def multiprocessing_example():
"""Истинный параллелизм на разных ядрах"""
processes = []
start = time.time()
# Создаём процесс для каждого ядра
for i in range(cpu_count()):
p = Process(target=cpu_intensive_task, args=(100_000_000,))
processes.append(p)
p.start()
for p in processes:
p.join()
print(f"Multiprocessing: {time.time() - start:.2f}s")
# На 4-ядерной машине: ~5 сек (истинный параллелизм)
multiprocessing_example()
Характеристики:
- Настоящий параллелизм (разные ядра)
- Каждый процесс имеет свой GIL
- Отличная для CPU-bound задач
- Высокий overhead (создание процесса дорого)
2. Многопоточность (Multithreading) — конкуренция на одном ядре
Несколько потоков в одном процессе, GIL позволяет переключаться между ними:
from threading import Thread, current_thread
import time
def io_bound_task(url):
"""I/O задача"""
print(f"Thread {current_thread().name} starting")
time.sleep(2) # Имитируем сетевой запрос
print(f"Thread {current_thread().name} done")
return f"Data from {url}"
def multithreading_example():
"""Конкуренция — потоки квазиодновременно"""
threads = []
start = time.time()
urls = ['api1', 'api2', 'api3', 'api4']
for url in urls:
t = Thread(target=io_bound_task, args=(url,), name=f"Fetcher-{url}")
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Multithreading: {time.time() - start:.2f}s")
# ~2 сек (потоки работают одновременно в I/O операциях)
multithreading_example()
Характеристики:
- Конкуренция, а не параллелизм
- Один GIL на весь процесс
- Хорошо для I/O-bound задач
- Низкий overhead (потоки дешевле чем процессы)
3. Асинхронность (Async/Await) — кооперативная многозадачность
Один поток, но код переключается между корутинами явно:
import asyncio
import time
async def async_io_task(name, delay):
"""Асинхронная задача"""
print(f"{name} starting")
await asyncio.sleep(delay) # Даёт контроль другим корутинам
print(f"{name} done")
return f"Result from {name}"
async def async_example():
"""Кооперативная многозадачность"""
start = time.time()
# Все корутины в одном потоке
tasks = [
async_io_task('Task1', 2),
async_io_task('Task2', 2),
async_io_task('Task3', 2),
async_io_task('Task4', 2),
]
results = await asyncio.gather(*tasks)
print(f"AsyncIO: {time.time() - start:.2f}s")
# ~2 сек (одновременно на одном потоке!)
asyncio.run(async_example())
Характеристики:
- Кооперативная многозадачность
- Один поток, один GIL
- Максимальная эффективность для I/O
- Минимальный overhead
4. Квантование времени (Time Slicing)
ОС переключает потоки через определённые интервалы:
import threading
import time
def task_with_time_slicing(task_id):
"""Задача, которая будет прерываться ОС"""
for i in range(3):
print(f"Task {task_id}: step {i}")
time.sleep(0.1) # Имитируем работу
# ОС может переключить контекст здесь
def time_slicing_example():
"""Операционная система переключает между потоками"""
threads = []
for i in range(3):
t = threading.Thread(target=task_with_time_slicing, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
time_slicing_example()
Это основа многопоточности — каждый поток получает квант времени.
5. Событийная многозадачность (Event-driven)
Задачи запускаются в ответ на события:
from typing import Callable, Dict, List
class EventLoop:
"""Простой event loop"""
def __init__(self):
self.handlers: Dict[str, List[Callable]] = {}
self.queue = []
def on(self, event: str, handler: Callable):
"""Подписаться на событие"""
if event not in self.handlers:
self.handlers[event] = []
self.handlers[event].append(handler)
def emit(self, event: str, data=None):
"""Вызвать обработчики события"""
if event in self.handlers:
for handler in self.handlers[event]:
handler(data)
def run(self):
"""Запустить event loop"""
print("Event loop running...")
while True:
pass # Слушаем события
# Использование
event_loop = EventLoop()
def on_user_created(user_data):
print(f"User created: {user_data}")
def on_user_deleted(user_id):
print(f"User deleted: {user_id}")
event_loop.on('user_created', on_user_created)
event_loop.on('user_deleted', on_user_deleted)
# Эмитируем события
event_loop.emit('user_created', {'id': 1, 'name': 'John'})
event_loop.emit('user_deleted', 123)
6. Зелёные потоки (Green Threads) — очень лёгкие потоки
Ось использует встроенные зелёные потоки (как в gevent):
from gevent import spawn, joinall
import gevent
import requests
def fetch(url):
"""Зелёный поток"""
print(f"Fetching {url}")
response = requests.get(url)
print(f"Got {url}")
return response.status_code
def green_threads_example():
"""Зелёные потоки — очень дешевые"""
urls = [
'http://example.com',
'http://google.com',
'http://github.com',
'http://stackoverflow.com',
]
# Создаём зелёные потоки
greenlets = [spawn(fetch, url) for url in urls]
# Ждём завершения
joinall(greenlets)
green_threads_example()
Зелёные потоки очень лёгкие — можно создать тысячи.
Сравнение всех видов
# Таблица сравнения
print("""
┌─────────────────┬──────────┬─────────┬─────────┬────────┐
│ Тип │ Параллл │ Одновр │ GIL │ Память │
├─────────────────┼──────────┼─────────┼─────────┼────────┤
│ Процессы │ Да │ Да │ Нет │ Много │
│ Потоки │ Нет │ Да │ Да │ Мало │
│ AsyncIO │ Нет │ Да │ Да │ Очень │
│ Зелёные потоки │ Нет │ Да │ Да │ Мало │
│ Event-driven │ Нет │ Да │ Да │ Мало │
└─────────────────┴──────────┴─────────┴─────────┴────────┘
""")
Практические рекомендации
# 1. CPU-bound (вычисления)
from multiprocessing import Pool
with Pool(4) as p:
results = p.map(expensive_calculation, data)
# 2. I/O-bound (сеть, БД, файлы) — AsyncIO
import asyncio
await asyncio.gather(*tasks)
# 3. I/O-bound (если не можешь использовать async)
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=5) as executor:
results = executor.map(blocking_io, data)
# 4. Когда нужны тысячи лёгких операций
from gevent import spawn, joinall
greenlets = [spawn(task, item) for item in items]
joinall(greenlets)
Когда использовать что
# Выбор стратегии многозадачности
if task_type == "CPU_BOUND":
# Вычисления, обработка данных
use("multiprocessing")
elif task_type == "IO_BOUND":
if can_use_async:
# Сеть, БД (асинхронные драйверы)
use("asyncio")
else:
if light_operations:
# Много лёгких операций
use("green_threads")
else:
# Блокирующий I/O
use("threading")
elif task_type == "EVENT_DRIVEN":
# Обработка событий
use("event_loop")
Итоговый ответ
Основные виды многозадачности:
- Многопроцессность — истинный параллелизм на разных ядрах (CPU-bound)
- Многопоточность — конкуренция в одном процессе (I/O-bound)
- AsyncIO — кооперативная многозадачность (I/O-bound, эффективнее потоков)
- Квантование времени — ОС переключает между потоками
- Event-driven — задачи на события (используется в веб-фреймворках)
- Green threads — очень лёгкие потоки (много параллельных операций)
Выбор зависит от типа задачи:
- Вычисления → многопроцессность
- I/O → asyncio (лучше) или многопоточность
- События → event loop
- Много лёгких операций → зелёные потоки