Вместо предисловия - рекомендуемую освежить в памяти посты:
- Fibers и виды многозадачности
- Сравнение видов многозадачности
Этот пост является продолжением этих постов. Из них узнаете в подробностях про вытесняющую многозадачность, которую будем разбирать сегодня.
_
Мы довольно много времени посвятили разбору примитивов синхронизации, их внутренностям и разбором разных "почему" и "зачем".
Сегодня хочется выйти из пузыря самих примитивов и посмотреть на среду в которой работают сами потоки и то как влияет синхронизация на эффективность программы.
🔵 Нечестная (unfair) блокировка
В прошлых постах я рассказывал про fast path - трюк в коде mutex благодаря которому улучшается производительность. Его суть - перед тем как засыпать поток какое то время пытается захватить мьютекс в надежде что тот освободится "вот вот". А если не получилось захватить, идем в спячку и ждем пока нас разбудят.
Видите ли вы в таком подходе намек на нечестную игру? Я да. Вместо того чтобы распределять процессорное время по принципу FIFO (очереди) у нас могут возникать ситуация, когда ресурс захватывает тот кто пришел последним и "угоняет" мьютекс пока те кто пришли раньше спят.
С точки зрения разработки прикладных программ может показаться что проблема надуманная, ведь благодаря этой оптимизации программы работают быстрее (меньше циклов засыпания, пробуждения и нагрузки на ОС). Ну и что что нет честности, зато быстро. И будете правы, но для полноты картины нам стоит рассмотреть другую сторону медали.
Примеры задач где нужна честная (fair) блокировка и строгие приоритеты.
Мир программирования не ограничивается прикладным софтом, помимо этого существуют:
- Станки с ЧПУ.
- Роботы.
- Задачи в реальном времени. Например работа с аудио и видео.
- Телеком.
- Системы критичные к latency (например трейдинг).
- Авиация, космос.
И вот для них описанный выше подход может быть неприемлем. Потому что при нечестной блокировке нет ни намека на предсказуемый отклик от нашей системы. Какой то поток занимающийся низкоприоритетной задачей может начать отъедать ресурс и привести к деградации системы.
🔵 Честная блокировка. Как к ней подобраться?
Несмотря на то что по умолчанию все внутренности ОС заточены на то чтобы всё работало быстро - у разработчиков есть возможность влиять на планирование потоков операционной системой и приоритеты.
🔵 Как изменить приоритет потока?
Делается это через вызов nice(2). Планировщик (CFS - Completely Fair Scheduler) использует установленные потокам значения при расчёте веса задачи (load_weight), определяя, сколько CPU времени ей выделять. Функция nice(2) применяется только для политики планирования по умолчанию (SCHED_OTHER в Linux). Принимает целочисленные значения в диапазоне [-20, +19]. Чем ниже значение тем больше веса у потока.
🔵 Политики планирования потоков
Помимо приоритетов у нас также есть возможность управлять планированием потоков через политики:
-
SCHED_OTHER или SCHED_NORMAL: Политика по умолчанию-
SCHED_IDLE: Более низкий приоритет, чем SCHED_OTHER- SCHED_FIFO: политика «первым пришел/первым ушел» в режиме реального времени (если в программе есть потоки с такой политикой в состоянии runnable - все остальные потоки с более слабой политикой будут немедленно остановлены / вытеснены).
-
SCHED_RR: Политика кругового перебора в реальном времени. Улучшение политики FIFO.Вот такой обзор получился, надеюсь мне удалось не слишком замудрено объяснить основную базу, чтобы вы могли в случае чего углубиться в вопрос самостоятельно. Буду рад если поделитесь обратной связью в комментариях, расскажите о своем опыте, вдруг вам будет чем дополнить материал.
Полезные ссылки:
- sched(7) — Linux manual page