Представьте, что Вы должны написать многопоточную программу на языке, в котором почти нет примитивов синхронизации. Единственное средство - функция TSL (test, set and lock), которая выглядит следующим образом:
Как реализовать с помощью этого примитива критическую секцию? Оптимально было бы получить реализацию двух функций:
Решение
Простейший вариант с активным ожиданием мог бы выглядеть так:
int tsl(int* value) {
int oldValue = *value;
*value = 1;
return oldValue;
}
При этом среда гарантирует, что функция атомарна. Например, что компилятор транслирует её в атомарную машинную инструкцию.Как реализовать с помощью этого примитива критическую секцию? Оптимально было бы получить реализацию двух функций:
lock()
и unlock()
.Решение
Простейший вариант с активным ожиданием мог бы выглядеть так:
int lock = 0;
void lock() {
while(!tsl(*lock)) {
// nothing
}
}
void unlock() {
*lock = 0;
}
В такой реализации unlock() не будет работать. Нужны барьеры.
ОтветитьУдалитьМожно представить, что tsl содержит acquire fence, но "*lock = 0;" уж точно не делает release.
Собственно, это следующий вопрос, который мы задаём на собеседовании :) Жаль только, что не все до него добираются.
ОтветитьУдалитьПо-моему, это вопрос на "знал - не знал". Думать тут нечего. Чем вы занимаетесь на работе, что для вас критично знание lock free алгоритмов?
ОтветитьУдалитьКстати, есть еще системы, на которых присваивание интов не атомарно. На них даже барьеры не спасут.
Задачу можно было бы переформулировать так: даны функции tsl и set_zero, нужно написать lock и unlock. После этого можно спросить, как в реальности можно реализовать tsl и set_zero и посмотреть, в курсе ли кандидат о CAS, барьерах, acquire и release семантике операций и т.п. Опять же, это предполагает, что знание lock free алгоритмов коррелирует с будущим успехом кандидата в компании, что спорно.
Занимаемся тем, что торгуем на валютном рынке, предлагая клиентам купить/продать валюту или производные инструменты.
ОтветитьУдалитьГрубо говоря, надо посмотреть текущий курс на бирже, предложить клиенту курс на десятую копейки в нашу сторону, а когда клиент заключит с нами сделку - бежать на биржу, пока разница в десятую копейки не превратилась в 0 или в минус 2 копейки.
Клиентов много (тысячи), валютных пар тоже много (несколько сотен), работать надо быстро. Вот и получается, что без хорошего знания concurrency человеку будет сложно влиться в проект.
Все работает.
ОтветитьУдалитьНепонятно при чем тут барьеры.
Только не *lock = 0; , а lock = 0; ,а то по защите вылетите.
Другое дело при такой организации порядок вхождения в критическую секцию детерминирован - поток может вечно "не успевать".
*недетерминирован (спеллчекер не сьел).
ОтветитьУдалитьБарьеры тут нужны.
ОтветитьУдалитьint shared_variable;
lock();
shared_variable = 42;
unlock();
Если unlock() не содержит release fence, side effects двух последних строчек могут произойти в обратном порядке. Что на практике будет выглядеть вот так:
lock();
unlock();
shared_variable = 42;
Упс.
*lock = 0 не вылетит по защите, а просто не скомпилируется. Даже если убрать звездочку, все равно не скомпилируется.