Нет, вы даже не можете предположить, что foo = p;
является атомным. Вполне возможно, что он может загрузить 16 бит 32-битного указателя, а затем заменить его перед загрузкой остальных.
Если другой поток проскользнет в этот момент и вызовет Instance()
, вы поджарены, потому что указатель foo
недействителен.
Для истинной безопасности вам придется защищать весь механизм тестирования и установки, даже если это означает использование мьютексов даже после создания указателя. Другими словами (и я предполагаю, что scoped_lock()
снимет блокировку, когда она выйдет из области видимости (у меня мало опыта с Boost)), что-то вроде:
Foo& Instance() {
scoped_lock lock(mutex);
if (foo != 0)
foo = new Foo();
return *foo;
}
Если вам не нужен мьютекс (по соображениям производительности, предположительно), я использовал опцию в прошлом, чтобы собрать все синглтоны до начала потоков.
Другими словами, предполагая, что у вас есть этот контроль (вы не можете), просто создайте экземпляр каждого синглтона в main
перед тем, как запустить другие потоки. Тогда вообще не используйте мьютекс. В этот момент у вас не возникнет проблем с многопоточностью, и вы можете просто использовать каноническую версию не заботиться о потоках:
Foo& Instance() {
if (foo != 0)
foo = new Foo();
return *foo;
}
И, да, это делает ваш код более опасным для людей, которые не удосужились прочитать ваши документы по API, но (IMNSHO) они заслуживают всего, что получают: -)