Add some python schemes
This commit is contained in:
parent
4e56f0f097
commit
62e533fec5
3 changed files with 316 additions and 0 deletions
140
python/schemes/multiprocessing.puml
Normal file
140
python/schemes/multiprocessing.puml
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
@startuml
|
||||
title Работа multiprocessing.Queue с большими объектами\n(2 worker процесса)
|
||||
|
||||
participant "Главный процесс" as Main
|
||||
participant "Queue\n(pipe buffer ~65KB)" as Queue
|
||||
participant "Worker 1" as W1
|
||||
participant "Worker 2" as W2
|
||||
|
||||
== Запуск процессов ==
|
||||
|
||||
Main -> W1 ** : start()
|
||||
Main -> W2 ** : start()
|
||||
|
||||
note over Main
|
||||
Оба worker'а работают
|
||||
параллельно в фоне
|
||||
end note
|
||||
|
||||
== Worker 1: Отправка больших данных ==
|
||||
|
||||
W1 -> W1 : Обработка данных
|
||||
activate W1 #lightblue
|
||||
W1 -> W1 : events = [...] (большой массив)
|
||||
W1 -> Queue : put({"events": events})\n[Начало сериализации]
|
||||
activate Queue #orange
|
||||
|
||||
note right of W1
|
||||
pickle сериализует объект:
|
||||
ОБЪЕКТ_1 → [500 KB байт]
|
||||
end note
|
||||
|
||||
W1 -> Queue : Запись chunk 1 (64 KB)
|
||||
W1 -> Queue : Запись chunk 2 (64 KB)
|
||||
|
||||
note over Queue
|
||||
Pipe buffer заполнен!
|
||||
Больше нельзя записать
|
||||
end note
|
||||
|
||||
W1 -[#red]> W1 : ⏸️ БЛОКИРОВКА на put()
|
||||
note right of W1 #pink
|
||||
Worker 1 ЖДЕТ,
|
||||
пока освободится место
|
||||
end note
|
||||
|
||||
== Worker 2: Тоже пытается отправить ==
|
||||
|
||||
W2 -> W2 : Обработка данных
|
||||
activate W2 #lightgreen
|
||||
W2 -> W2 : events = [...] (большой массив)
|
||||
W2 -> Queue : put({"events": events})
|
||||
W2 -[#red]> W2 : ⏸️ БЛОКИРОВКА\n(очередь занята Worker 1)
|
||||
|
||||
== Главный процесс: Чтение (итерация 1) ==
|
||||
|
||||
Main -> Main : for i in range(2):\n result = queue.get()
|
||||
activate Main #yellow
|
||||
|
||||
Main -> Queue : get() [вызов]
|
||||
note right of Main #lightgreen
|
||||
Первая итерация цикла
|
||||
i = 0
|
||||
end note
|
||||
|
||||
Queue -> Queue : Читает ВСЕ байты ОБЪЕКТА_1
|
||||
note over Queue
|
||||
✓ Читает chunk 1 (64 KB)
|
||||
✓ Читает chunk 2 (64 KB)
|
||||
✓ Читает chunk 3...
|
||||
...
|
||||
✓ Читает последний chunk
|
||||
|
||||
ВАЖНО: get() не вернется,
|
||||
пока не прочитает ВЕСЬ объект!
|
||||
end note
|
||||
|
||||
Queue -> Main : return ПОЛНЫЙ ОБЪЕКТ_1
|
||||
note right of Main
|
||||
Получен ЦЕЛЫЙ объект
|
||||
от Worker 1
|
||||
end note
|
||||
|
||||
note over Queue #lightgreen
|
||||
Pipe buffer освобожден!
|
||||
Теперь Worker 1 может
|
||||
продолжить запись
|
||||
end note
|
||||
|
||||
W1 -[#green]> W1 : ✓ Разблокировка!
|
||||
W1 -> W1 : put() завершен
|
||||
deactivate W1
|
||||
|
||||
note over Queue
|
||||
Теперь очередь пустая,
|
||||
Worker 2 может записать
|
||||
end note
|
||||
|
||||
W2 -[#green]> W2 : ✓ Разблокировка!
|
||||
W2 -> Queue : Запись ОБЪЕКТА_2
|
||||
activate Queue #orange
|
||||
W2 -> W2 : put() завершен
|
||||
deactivate W2
|
||||
|
||||
== Главный процесс: Чтение (итерация 2) ==
|
||||
|
||||
Main -> Queue : get() [второй вызов]
|
||||
note right of Main #lightgreen
|
||||
Вторая итерация цикла
|
||||
i = 1
|
||||
end note
|
||||
|
||||
Queue -> Queue : Читает ВСЕ байты ОБЪЕКТА_2
|
||||
Queue -> Main : return ПОЛНЫЙ ОБЪЕКТ_2
|
||||
deactivate Queue
|
||||
|
||||
note right of Main
|
||||
Получен ЦЕЛЫЙ объект
|
||||
от Worker 2
|
||||
|
||||
ДАННЫЕ НЕ СМЕШАЛИСЬ!
|
||||
Каждый get() вернул
|
||||
полный объект
|
||||
end note
|
||||
|
||||
== Завершение ==
|
||||
|
||||
Main -> W1 : join()
|
||||
W1 --> Main : ✓ завершен
|
||||
Main -> W2 : join()
|
||||
W2 --> Main : ✓ завершен
|
||||
|
||||
deactivate Main
|
||||
|
||||
note over Main, W2 #lightgreen
|
||||
✓ Все данные получены полностью
|
||||
✓ Ничего не потеряно
|
||||
✓ Объекты не смешались
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
@startuml gil_switching_mechanism
|
||||
title GIL Switching - что происходит на микроуровне
|
||||
|
||||
participant "Event Loop\n(Main Thread)" as EL
|
||||
participant "Python\nRuntime" as PY
|
||||
participant "Worker Thread" as WT
|
||||
participant "GIL" as GIL
|
||||
participant "OS Kernel" as OS
|
||||
|
||||
== Запуск Worker Thread ==
|
||||
|
||||
EL -> WT: asyncio.to_thread(requests.get)
|
||||
activate WT
|
||||
|
||||
note over EL #90EE90
|
||||
Event Loop продолжает работать!
|
||||
Ждёт своей очереди на GIL
|
||||
end note
|
||||
|
||||
== Фаза 1: Worker Thread стартует (Python код) ==
|
||||
|
||||
WT -> GIL: Захватывает GIL
|
||||
note over GIL #FFD700: Worker Thread держит GIL
|
||||
|
||||
WT -> WT: import requests
|
||||
WT -> WT: подготовка параметров
|
||||
WT -> WT: создание Request объекта
|
||||
|
||||
note over EL #FFD700
|
||||
Event Loop ХОЧЕТ GIL,
|
||||
но ждёт своей очереди
|
||||
end note
|
||||
|
||||
PY -> PY: Прошло ~5ms или 100 bytecode инструкций
|
||||
|
||||
note over PY #90EE90
|
||||
**GIL SWITCHING!**
|
||||
Python runtime автоматически
|
||||
переключает GIL между потоками
|
||||
end note
|
||||
|
||||
PY -> WT: Отпусти GIL
|
||||
WT -> GIL: Освобождает GIL (временно)
|
||||
GIL -> EL: Захватывает GIL
|
||||
|
||||
note over EL #90EE90
|
||||
Event Loop работает!
|
||||
Обрабатывает задачи
|
||||
end note
|
||||
|
||||
EL -> EL: await task1
|
||||
EL -> EL: await task2
|
||||
|
||||
PY -> PY: Прошло ~5ms
|
||||
|
||||
PY -> EL: Отпусти GIL
|
||||
EL -> GIL: Освобождает GIL
|
||||
GIL -> WT: Захватывает GIL
|
||||
|
||||
note over WT
|
||||
Worker Thread продолжает
|
||||
подготовку к I/O
|
||||
end note
|
||||
|
||||
== Фаза 2: Системный вызов (I/O) ==
|
||||
|
||||
WT -> WT: socket.connect()
|
||||
note over WT #90EE90: Это системный вызов!
|
||||
|
||||
WT -> GIL: **Полностью освобождает GIL**
|
||||
note over GIL #90EE90
|
||||
🔓 GIL СВОБОДЕН!
|
||||
Нет конкуренции
|
||||
end note
|
||||
|
||||
WT -> OS: socket.recv() - ждёт данные
|
||||
|
||||
note over OS
|
||||
Ядро читает данные
|
||||
из сети
|
||||
(секунды!)
|
||||
end note
|
||||
|
||||
par Event Loop работает БЕЗ конкуренции
|
||||
EL -> GIL: Захватывает GIL
|
||||
EL -> EL: await task1 ✅
|
||||
EL -> EL: await task2 ✅
|
||||
EL -> EL: await task3 ✅
|
||||
note over EL #90EE90
|
||||
Полная скорость!
|
||||
GIL не занят
|
||||
end note
|
||||
end
|
||||
|
||||
== Фаза 3: Данные получены ==
|
||||
|
||||
OS --> WT: Данные получены
|
||||
WT -> GIL: Захватывает GIL опять
|
||||
WT -> WT: обработка ответа (Python код)
|
||||
|
||||
note over WT,GIL #FFD700
|
||||
Снова конкуренция за GIL
|
||||
(но это быстро - десятки мс)
|
||||
end note
|
||||
|
||||
PY -> PY: GIL switching работает
|
||||
|
||||
WT -> WT: возврат результата
|
||||
WT --> EL: Результат готов
|
||||
deactivate WT
|
||||
|
||||
note over EL,WT #90EE90
|
||||
**Итого:**
|
||||
- Фаза 1 (подготовка): ~10-50ms, GIL switching
|
||||
- Фаза 2 (I/O): секунды, GIL свободен
|
||||
- Фаза 3 (обработка): ~10-50ms, GIL switching
|
||||
|
||||
Основное время - I/O, где GIL свободен!
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
@startuml scenario2_io_with_tothread
|
||||
title I/O операция С to_thread() - Event Loop свободен ✅
|
||||
|
||||
participant "Event Loop\n(Main Thread)" as EL
|
||||
participant "Worker Thread" as WT
|
||||
participant GIL
|
||||
participant "OS Kernel" as OS
|
||||
|
||||
note over EL #90EE90: Event Loop работает
|
||||
|
||||
EL -> WT: asyncio.to_thread(requests.get)
|
||||
activate WT
|
||||
|
||||
note over EL #90EE90
|
||||
**Event Loop СВОБОДЕН!**
|
||||
Обрабатывает другие задачи
|
||||
end note
|
||||
|
||||
group Event Loop продолжает работу
|
||||
EL -> EL: await других async задач ✅
|
||||
EL -> EL: обработка новых запросов ✅
|
||||
EL -> EL: работа продолжается ✅
|
||||
end
|
||||
|
||||
group Worker Thread делает I/O
|
||||
WT -> GIL: Захватывает GIL
|
||||
WT -> OS: socket.recv()
|
||||
|
||||
note over WT,GIL #90EE90
|
||||
🔓 **GIL ОСВОБОЖДАЕТСЯ**
|
||||
(внутри C-кода requests)
|
||||
Python интерпретатор ждёт OS
|
||||
end note
|
||||
|
||||
note over OS
|
||||
Ядро читает данные
|
||||
с сетевого интерфейса
|
||||
end note
|
||||
|
||||
OS --> WT: Данные получены
|
||||
WT -> GIL: Снова захватывает GIL
|
||||
end
|
||||
|
||||
WT --> EL: Результат готов
|
||||
deactivate WT
|
||||
EL -> EL: получает результат через await
|
||||
|
||||
note over EL,WT #90EE90
|
||||
**Ключевой момент:**
|
||||
Пока Worker Thread ждёт OS,
|
||||
Event Loop продолжает работать!
|
||||
GIL освобождается во время системных вызовов
|
||||
end note
|
||||
@enduml
|
||||
Loading…
Reference in a new issue