Singleton* Singleton::instance() {
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
Singleton* temp = new Singleton; // initialize to temp
pInstance = temp; // assign temp to pInstance
}
}
Предположим, что компилятор не оптимизирует избыточную температуру. В потоке A находится и выделяется и строится объект Singleton, этот объект указывается temp. Теперь А выгружен сразу после этого. Теперь поток B получает блокировку, встает и проверяет, что pInstance имеет значение NULL. Он также создаст объект Singleton и запишет существующий указатель. Я предполагаю, что есть утечка памяти. Каково твое мнение? Полный источник находится здесь: Код ссылки: http://erdani.com/publications/DDJ_Jul_Aug_2004_revised.pdf
В С++ 11 стандарт гласит в пункте 6.7, что:
такая переменная инициализируется, когда первый контроль проходит через его объявление; такая переменная считается инициализированной после завершения ее инициализации. [...] Если элемент управления входит в объявление одновременно, когда переменная инициализируется, одновременное выполнение должно ждать завершения инициализации.
Реализация не должна вводить какой-либо тупик вокруг выполнения инициализатора.
Это приводит к следующей очень простой и потокобезопасной реализации однопользовательского метода:
Singleton* Singleton::instance() {
Singleton instance;
return &instance;
}
См. Этот вопрос для получения дополнительной информации о том, какие компиляторы поддерживают это.
Нет. Когда A прерван, на пороге есть замок. Таким образом, B должен ждать, пока A освободит блокировку, но затем назначается pInstance и B второй проверяет, что null будет терпеть неудачу.