Обычный шаблон для singleton-класса - что-то как
static Foo &getInst()
{
static Foo *inst = NULL;
if(inst == NULL)
inst = new Foo(...);
return *inst;
}
Однако это - мое понимание, что это решение не ориентировано на многопотоковое исполнение, так как 1) конструктора Foo можно было бы вызвать несколько раз (который может или не может иметь значения), и 2) inst не может быть полностью создан, прежде чем это будет возвращено к другому потоку.
Одно решение состоит в том, чтобы перенести взаимное исключение вокруг целого метода, но затем я плачу за синхронизацию наверху еще долго после того, как мне на самом деле нужен он. Альтернатива - что-то как
static Foo &getInst()
{
static Foo *inst = NULL;
if(inst == NULL)
{
pthread_mutex_lock(&mutex);
if(inst == NULL)
inst = new Foo(...);
pthread_mutex_unlock(&mutex);
}
return *inst;
}
Действительно ли это - правильный способ сделать это или является там какими-либо ловушками, о которых я должен знать? Например, есть ли какие-либо статические проблемы порядка инициализации, которые могли бы произойти, т.е. inst, как всегда гарантируют, будет, АННУЛИРУЮТ в первый раз getInst, назван?
Ваше решение называется «блокировка с двойной проверкой», и то, как вы его написали, не является потокобезопасным.
Эта статья Мейерса / Александреску объясняет, почему - но эта статья также широко неправильно понимается. Он запустил мем «двойная проверка небезопасно в C ++», но его фактический вывод состоит в том, что двойная проверяемая блокировка в C ++ может быть реализована безопасно, для этого просто требуется использование барьеров памяти в неочевидном месте.
Документ содержит псевдокод, демонстрирующий, как использовать барьеры памяти для безопасной реализации DLCP, поэтому вам не составит труда исправить свою реализацию.
Используйте pthread_once
, что является гарантировано, что функция инициализации запускается один раз атомарно.
(В Mac OS X используется спин-блокировка. Не знаю реализации на других платформах.)
TTBOMK, единственный гарантированный потокобезопасный способ сделать это без блокировки - это инициализировать все ваши синглтоны до того, как вы когда-нибудь запустите нить.
Ваш вариант называется «блокировка с двойной проверкой» .
Могут существовать модели многопоточной памяти, в которых она работает, но POSIX не гарантирует ее.
Одноэлементная реализация ACE использует шаблон блокировки с двойной проверкой для обеспечения безопасности потоков, вы можете обратиться к нему, если хотите.
Вы можете найти исходный код здесь .