Безопасна ли реализация синглтона с двойной проверкой потока?

Я знаю, что общая реализация поточно-ориентированного синглтона выглядит так:

Singleton* Singleton::instance() {
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
Singleton* temp = new Singleton; // initialize to temp
pInstance = temp; // assign temp to pInstance
}
}
return pInstance;
}

Но почему они говорят, что это поточно-ориентированная реализация?
Например, первый поток может пройти оба теста на pInstance == 0, Создайте new Singleton и назначить его temp указатель, а затем Начните назначение pInstance = temp (насколько я знаю, операция присваивания указателя не является атомарной).
В то же время второй поток тестирует первый pInstance == 0, где pInstance назначается только половина. Это не nullptr, но и недопустимый указатель, который затем возвращается из функции.
Может ли такая ситуация случиться? Я нигде не нашел ответа, и кажется, что это вполне правильная реализация, и я ничего не понимаю

3

Решение

Это не безопасно по правилам параллелизма C ++, так как первое чтение pInstance не защищен блокировкой или чем-то подобным и, следовательно, не синхронизируется правильно с записью (которая является защищенный). Таким образом, существует гонка данных и, следовательно, неопределенное поведение. Одним из возможных результатов этого UB является именно то, что вы определили: первая проверка, считывающая значение мусора pInstance который просто пишется другим потоком.

Общее объяснение состоит в том, что это экономит на получении блокировки (потенциально дорогостоящая операция) в более распространенном случае (pInstance уже действует). Тем не менее, это не безопасно.

Поскольку C ++ 11 и более поздние версии гарантируют, что инициализация статических переменных области действия происходит только один раз и является поточно-ориентированной, лучший способ создания синглтона в C ++ состоит в том, чтобы иметь статическую локальную переменную в функции:

Singleton& Singleton::instance() {
static Singleton s;
return s;
}

Обратите внимание, что нет необходимости ни в динамическом размещении, ни в типе возвращаемого указателя.


Как Voo упомянутое в комментариях, вышеизложенное предполагает pInstance это необработанный указатель Если бы это было std::atomic<Singleton*> вместо этого код будет работать нормально, как задумано. Конечно, тогда возникает вопрос, намного ли медленнее атомарное чтение, чем получение блокировки, на что нужно ответить через профилирование. Тем не менее, это было бы довольно бессмысленным упражнением, поскольку статические локальные переменные лучше во всех случаях, кроме очень неясных.

4

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector