, я наткнулся на несколько озадачевшею меня реализацию Double-checked locking:
#include <atomic>
#include <mutex>
class Singleton {
public:
Singleton* GetInstance();
private:
Singleton() = default;
static std::atomic<Singleton*> s_instance;
static std::mutex s_mutex;
};
Singleton* Singleton::GetInstance() {
Singleton* p = s_instance.load(std::memory_order_acquire);
if (p == nullptr) {
std::lock_guard<std::mutex> lock(s_mutex);
p = s_instance.load(std::memory_order_relaxed); // <- этоif (p == nullptr) {
p = new Singleton();
s_instance.store(p, std::memory_order_release);
}
}
return p;
}
В принципе всё логично и работать оно будет, но какой в данном случае смысл в std::memory_order_relaxed под мьютексом? Мьютекс же работает как барьер и в любом случае чтение пройдет как std::memory_order_acquire? Или я что-то путаю?
KP>В принципе всё логично и работать оно будет, но какой в данном случае смысл в std::memory_order_relaxed под мьютексом? Мьютекс же работает как барьер и в любом случае чтение пройдет как std::memory_order_acquire? Или я что-то путаю?
Умение задавать вопросы в которых 100% ответа — это своего рода талант.
Здравствуйте, σ, Вы писали:
KP>>В принципе всё логично и работать оно будет, но какой в данном случае смысл в std::memory_order_relaxed под мьютексом? Мьютекс же работает как барьер и в любом случае чтение пройдет как std::memory_order_acquire? Или я что-то путаю?
σ>Умение задавать вопросы в которых 100% ответа — это своего рода талант.
KP>В принципе всё логично и работать оно будет, но какой в данном случае смысл в std::memory_order_relaxed под мьютексом? Мьютекс же работает как барьер и в любом случае чтение пройдет как std::memory_order_acquire? Или я что-то путаю?
Это оптимизация. memory_order_relaxed -- самый дешёвый вид чтения. На x86 это просто считывание переменной. Поскольку оно происходит под мьютексом, никаких дополнительных гарантий не нужно.
Здравствуйте, Шахтер, Вы писали:
Ш>Это оптимизация. memory_order_relaxed -- самый дешёвый вид чтения. На x86 это просто считывание переменной. Поскольку оно происходит под мьютексом, никаких дополнительных гарантий не нужно.
Я немного о другом. Разве мьютекс не является одновременно барьером? Если он является таковым, то memory_order_relaxed в данном случае не вносит никакой оптимизации.
Если же утверждение выше не верно, то хотелось бы понять каким образом memory_order_relaxed привносит здесь оптимизацию?
Здравствуйте, kaa.python, Вы писали:
KP>Здравствуйте, Шахтер, Вы писали:
Ш>>Это оптимизация. memory_order_relaxed -- самый дешёвый вид чтения. На x86 это просто считывание переменной. Поскольку оно происходит под мьютексом, никаких дополнительных гарантий не нужно.
KP>Я немного о другом. Разве мьютекс не является одновременно барьером? Если он является таковым, то memory_order_relaxed в данном случае не вносит никакой оптимизации.
KP>Если же утверждение выше не верно, то хотелось бы понять каким образом memory_order_relaxed привносит здесь оптимизацию?
Я не понимаю, чего здесь не понятного? Для считывания атомика нужно вызвать load с флагом. Флаг определяет дополнительные телодвижения, которые компилятор сгенерирует для соблюдения соответствующих гарантий.
Самый дешёвый флаг memory_order_relaxed. Он даёт только одну гарантию -- атомарность операции.
, я наткнулся на несколько озадачевшею меня реализацию Double-checked locking:
По мотивам подобного срача рефакторинга окаменелых говен, хотелось бы задать публике вопрос — имеет ли подобный ментальный онанизм красивый паттерн вообще какой-то стратегический смысл?
Постановка вопроса подразумевает, что объект под синглтоном делает что-то полезное и используется из разных потоков. Но делающий что-то полезное объект в данном случае должен сам обеспечивать потокобезопасность своих методов. Что в большинстве случае означает, явный или неявный lock внутри делающих что-то полезное методов.
Здравствуйте, landerhigh, Вы писали:
L>Короче, за что боремся?
Да меня сама конструкция заинтересовала, вариант использования std::memory_order_relaxed. Так что можно считать это не вопросом про синглтон, а вопросом про std::memory_order_relaxed и мьютексы
Здравствуйте, kaa.python, Вы писали:
KP>В принципе всё логично и работать оно будет, но какой в данном случае смысл в std::memory_order_relaxed под мьютексом? Мьютекс же работает как барьер и в любом случае чтение пройдет как std::memory_order_acquire? Или я что-то путаю?
Acquire был раньше, когда мьютекс захватывался. А теперь можно, пока под мьютексом, прочесть максимально дешёвым и быстрым образом и не получить за это по рыжей морде каким-нибудь обгоном. Поэтому — relaxed.
Иначе бы получалось, что мьютекс уже захвачен, и снова вдруг подымается машина сериализации операций — а нафига собственно?
$>Здравствуйте, Шахтер, Вы писали:
Ш>>Я не понимаю, чего здесь не понятного? Для считывания атомика нужно вызвать load с флагом.
$>Почему бы обычный volatile не использовать? Ведь буков меньше и на до- C++11 компиляторах соберётся.
Не ожидал такого вопроса в 2019 году... Рекомендую обратиться к первоисточникам.
Здравствуйте, Vamp, Вы писали:
V>$>Почему бы обычный volatile не использовать? Ведь буков меньше и на до- C++11 компиляторах соберётся. V>Не ожидал такого вопроса в 2019 году... Рекомендую обратиться к первоисточникам.
Безотносительно того, что volatile тут не поможет, хотелось бы заметить, что по коду сразу не понятно в каком году он написан, но судя по тому что в нем все еще гуляют голые указатели, а не std::unique_ptr — явно раньше 11-го года
И вообще, в С++11 уже давно появился std::call_once, который без лишних вопросов, должен решить проблему создания одного объекта.
Здравствуйте, netch80, Вы писали:
N>Acquire был раньше, когда мьютекс захватывался. А теперь можно, пока под мьютексом, прочесть максимально дешёвым и быстрым образом и не получить за это по рыжей морде каким-нибудь обгоном. Поэтому — relaxed. N>Иначе бы получалось, что мьютекс уже захвачен, и снова вдруг подымается машина сериализации операций — а нафига собственно?
а где нибудь можно про это почитать в систематизированном виде ?
те не в викпедии
Здравствуйте, sergey2b, Вы писали:
S>Здравствуйте, netch80, Вы писали:
N>>Acquire был раньше, когда мьютекс захватывался. А теперь можно, пока под мьютексом, прочесть максимально дешёвым и быстрым образом и не получить за это по рыжей морде каким-нибудь обгоном. Поэтому — relaxed. N>>Иначе бы получалось, что мьютекс уже захвачен, и снова вдруг подымается машина сериализации операций — а нафига собственно?
S>а где нибудь можно про это почитать в систематизированном виде ? S>те не в викпедии
$>Здравствуйте, netch80, Вы писали:
N>>До C++11 можно было платформенно-зависимые функции применять.
$>Ага, InterlockedExchange. Подзабыл уже.
При чём тут это? InterlockedExchange не имеет уже никакого смысла, если мы под мьютексом, который защищает доступ к переменной (и все участники соблюдают это). Он нужен, если мы стараемся обойти без мьютекса — но в этом случае в C++11 есть свой atomic_exchange.
Про платформенно-зависимые я имел в виду, что где-то pthread_mutex_lock, где-то WaitForSingleObject над мьютексом.
А вы таки путаетесь в самых основах, лучше повторить.
$>На Java достаточно volatile.
И снова мимо. Явовский volatile ближе всего к std::atomic<> load/store с memory_order_seq_cst.
Но он не даст защиты больше чем на одно чтение/запись. Аналог доступа под мьютексом — это synchronized.