В C ++ и опасностях двойной проверки блокировки , существует код персудо для правильной реализации шаблона, который предлагается авторами. Смотри ниже,
Singleton* Singleton::instance () {
Singleton* tmp = pInstance;
... // insert memory barrier (1)
if (tmp == 0) {
Lock lock;
tmp = pInstance;
if (tmp == 0) {
tmp = new Singleton;
... // insert memory barrier (2)
pInstance = tmp;
}
}
return tmp;
}
Мне просто интересно, можно ли переместить первый барьер памяти прямо над оператором return?
РЕДАКТИРОВАТЬ : Еще один вопрос: в связанной статье, как vidstige цитируется
Технически, вам не нужны полные двунаправленные барьеры. Первый барьер
должны предотвратить миграцию вниз синглтона
(другой веткой); второй барьер должен предотвращать миграцию вверх
инициализации экземпляра. Это называется «приобрести» и
«Релиз» операций, и может дать лучшую производительность, чем полный
барьеры на аппаратных средствах (таких как Itainum), которые делают различие.
В нем говорится, что второй барьер не должен быть двунаправленным, так как же он может предотвратить перемещение назначения pInstance до этого барьера? Хотя первый барьер может помешать миграции вверх, но другой поток все еще может иметь шанс увидеть неинициализированные элементы.
РЕДАКТИРОВАТЬ : Мне кажется, я почти понимаю цель первого барьера. Как отмечено sonicoder , предсказание ветвления может привести к тому, что tmp станет NULL, если if вернет true. Чтобы избежать этой проблемы, должен быть барьер получения, чтобы предотвратить чтение tmp взамен перед чтением в if.
Первый барьер соединяется со вторым барьером для достижения синхронизации с отношением , поэтому его можно перемещать вниз.
РЕДАКТИРОВАТЬ : Для тех, кто интересуется этим вопросом, я настоятельно рекомендую прочитать memory-barriers.txt .