Как стать автором
Обновить
0

Недостаточно знать, что такое Mutex, Semaphore и async/await. Надо знать всё, начиная с квантов

Время на прочтение3 мин
Количество просмотров15K

Совсем скоро, 29-30 ноября в Санкт-Петербурге и 06-07 декабря — в Москве мы запустим шестой семинар по .NET. На этот раз — по теме многопоточки и конкурентности. Мы уже писали об этом пару раз на Хабре, но сегодня есть отдельный повод для этого: на семинаре настоящий эксклюзив. Будет описана работа гибридного примитива синхронизации: Monitor. Да, всем привычная вещица достойна отдельного доклада. Ведь он в своей работе учитывает и частоту процессора и количество ядер, учитывает lock convoy/starvation и вообще, очень сложен.


А в конце статьи развлечения ради предложу пройти парочку QUIZов по многопоточке.



Небольшой сценарий с семинара


Самое важное, что исходит от операционной системы — это планирование потоков. Ведь они могут работать как в параллели друг с другом (когда в данный момент исполняются на разных ядрах), так и что чаще (если речь идёт об одних и тех же потоках) — последовательно. Ведь ОС даёт не так много времени на исполнение — каждому, после чего отдает время другим. Второе — для этого отрезка — кванта — может быть выделено разное количество времени. Например, в зависимости от того, какой версией ОС мы пользуемся: серверной или пользовательской, является ли поток UI потоком процесса с текущим активным окном. Третье — есть приоритеты и понятие "вытесняющая многозадачность", когда ваш поток, только получив заветный квант может его потерять, т.к. образовался другой поток с более высоким приоритетом. Получается, что наше приложение сильно зависит от того, в каком окружении оно работает. Например, какой-нибудь расчётный сервис лучше всего будет себя чувствовать на серверном варианте ОС (либо с соответствующими настройками производительности), когда на машине нет вообще никаких других сервисов: кванты будут длинными, квантового времени будет предоставлено много.


Но тут встаёт другой вопрос: если наш поток на такой конфигурации устанавливает блокировку уровня ядра (например, Semaphore(1) ), второй поток дойдя до установки блокировки у себя в эту блокировку встаёт, то стоять он в серверной ОС будет намного дольше, чем стоял бы в пользовательской. Почему? Да потому что квант времени серверной в 6 раз длиннее, чем у клиентской и этому потоку придется сначала подождать, когда первый поток дойдёт до места снятия блокировки, а потом — когда ему выдадут новый квант.


Однако, есть помощь и для такого случая: при снятии блокировки у всех потоков, кто её ожидал, временно (на 1 квант) повышается приоритет над другими потоками и второй поток получит процессорное время сразу.


CLRium 6


Эти три абзаца — это сжатые 5% от 4-го доклада. А он уже богат на информацию, которой можно воспользоваться на всех уровнях: от работы с примитивами синхронизации до работы с высокоуровневыми библиотеками. А программа у нас такая:


  1. Мы посмотрим на типы процессов. Ведь их много, а пользуемся мы от силы двумя из них: это обычные и ModernApp;
  2. Три доклада подряд — это потоки уровня операционной системы: планирование на одноядерных, многоядерных системах и NUMA системах. Везде правила — разные и это надо учитывать в своей работе;
  3. Разбор работы примитивов синхронизации на уровне квантового времени. Говорить о lock/Mutex/Semaphore вы все научились на собеседованиях. Нет никакого смысла повторяться, а потому мы вооружимся временными графиками и посмотрим, как распределяются кванты между процессорами на всех примитивах синхронизации: Kernel-Space, User-Space и гибридных.
  4. Настоящий эксклюзив семинара: строение примитива Monitor. То, что lock(){} раскрывается в try { } finally { } вы и без меня знали, а вот во что раскрывается Monitor.Enter, Monitor.Leave, Monitor.TryEnter — это тема для отдельного, плотного, полного ада доклада. Поверьте, внутри у него всё очень и очень круто. Это — гибридный примитив синхронизации, который построен избегать Starvation, излишних уходов в блокировку и lock convoy.
  5. Целых три плотных доклада по lock-free и wait-free в том числе на примере разведовательных дронов и ПВО, пытающейся сбить эти дроны. И эти доклады настолько понравились HighLoad++, что их позвали на HighLoad++ в Москве 07-08 ноября.
  6. Ряд докладов по PLINQ и Async-Await. Всё тоже максимально подробно. Не по вешкам: этого добра хватает в Интернете. Каждая технология будет рассказана "изнутри": как это принято на CLRium.
  7. И закроют семинар два доклада по библиотеке lock-free коллекций от Microsoft и Intrinsics (векторные инструкции для параллелизации на уровне процессора).

Немного статистики


Мы — крупнейший семинар страны и в общем не являемся конференцией только потому, что нам нравится наш формат. Вы не выбираете среди докладов, на которые вы не пойдёте. Вы идёте на все. При этом вы заранее понимаете, что все темы семинара вам интересны, т.к. тема едина. На CLRium 6 будет поставлен очередной рекорд: в обоих городах будет присутствовать 700 человек. Около 700 человек прокачают свои навыки в параллелизации и работой с конкурентностью. И пойдут на собеседования. Приходите и вы к нам :).

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Как работают потоки?
3.35% Процессор имеет несколько чипов, которые одновременно выполняют команды. Квант времени — это фиксированный отрезок времени чтобы замерять производительность системы6
9.5% Процессор имеет механизм нитей, на которые операционная система навешивает реальные потоки. Квант времени — это фиксированная величина, также используется как performance величина, относительно которой перераспределяется нагрузка между нитями17
0.56% Процессор ничего не знает о потоках, он знает только о процессах, которые отрабатывают на отдельных чипах внутри процессора, каждый — за определенное время, называемое квантом. После того как отработан квант времени, меняется активный чип, выполняющий код1
2.23% Процессор ничего не знает о потоках, он знает только о процессах, которые нанизываются на механизм нитей процессора. Механизм вытесняющей многозадачности — это механизм, при котором процессор выбирает самую сложную по выполнению за определенный квант времени задачу4
12.29% Процессор ничего не знает ни о потоках, ни о процессах. Он просто последовательно выполняет инструкции, а операционная система самостоятельно меняет инструкции таким образом, чтобы они выполнялись по одной с каждой задачи и потока, эмулируя т.о. параллельное исполнение22
15.64% Процессор ничего не знает ни о потоках, ни о процессах. Он просто последовательно выполняет инструкции, а операционная система самостоятельно меняет группы инструкций таким образом, чтобы они выполнялись за определенный отрезок времени — квант. На разных процессорах при этом код исполняется гарантированно параллельно28
56.42% Процессор ничего не знает ни о потоках, ни о процессах. Он просто последовательно выполняет инструкции, а операционная система самостоятельно подставляет для него группы инструкций таким образом, чтобы они выполнялись им за определенный отрезок времени — квант, после чего меняет группу. На разных процессорах при этом код исполняется гарантированно параллельно, но только если он не работает с единой памятью в рамках SMT или NUMA узлов101
Проголосовали 179 пользователей. Воздержались 65 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Что такое lock(){}? (как если бы не читали эту статью)
34.39% lock — это синтаксический сахар вокруг Mutex54
22.29% lock — это user-space примитив синхронизации, работа которого основывается на хранении состояния в SyncBlockIndex35
17.83% lock — это гибридный примитив синхронизации, который может уходить в ядро ОС, но от того подвержен lock convoy и starvation28
7.01% lock работает на Semaphore, но если работает TryEnter, может использовать CAS конструкции11
7.01% TryEnter всегда User-Space, тогда как Enter может использовать Semaphore для входа в режим ядра. При входе в режим ядра возникают риски lock convoy/starvation11
11.46% TryEnter всегда User-Space, тогда как Enter может использовать Semaphore для входа в режим ядра. lock convoy/starvation обходятся внутренними алгоритмами18
Проголосовали 157 пользователей. Воздержались 73 пользователя.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Блокировка уровня ядра
11% Рудиментарная блокировка, т.к. её вызов уходит в ядро, а значит тратится много времени на переход в kernel space и обратно. Лучше пользоваться Semaphore, ManualResetEvent/AutoResetEvent11
4% Рудиментарная блокировка, т.к. её вызов уходит в ядро, а значит тратится много времени на переход в kernel space и обратно. Лучше пользоваться ReaderWriterLock и прочими4
24% Рудиментарная блокировка, т.к. её вызов уходит в ядро, а значит тратится много времени на переход в kernel space и обратно. Лучше пользоваться Slim версиями, т.к. они хотя-бы пытаются взять блокировку в user space24
7% Иногда полезная блокировка, в особенности на дальних дистанциях, но т.к. ядро — это CLR, то поток на время блокировки переиспользуется другим кодом7
8% Полезная блокировка, т.к. освобождает кванты времени другим потокам. Но т.к. вход в режим ядра занимает время, используется крайне редко. Лучше всегда пользоваться user-space конструкциями8
46% Полезная блокировка, т.к. освобождает кванты времени другим потокам. Но т.к. вход в режим ядра занимает время, для многих сценариев подойдут гибридные версии. Однако, когда блокировка гарантированно встанет на долго, будет правильным вариантом.46
Проголосовали 100 пользователей. Воздержались 68 пользователей.
Теги:
Хабы:
Всего голосов 32: ↑28 и ↓4+24
Комментарии9

Публикации

Информация

Сайт
clrium.ru
Дата регистрации
Дата основания
Численность
1 человек (только я)
Местоположение
Россия
Представитель
Stanislav Sidristij

Истории