ОК, так что вы вообще не можете сделать это без мьютекса, но вы можете сделать этот мьютекс быстрым.
Сначала объявите класс для хранения мьютекса и флаг готовности (InitMutex
ниже). Когда вызывается GrabMutex()
и ready
равно false
, фактический мьютекс захватывается. Когда вызывается ReleaseMutex()
, он делает все правильно, основываясь на флаге, отправленном с GrabMutex()
. После первого выполнения готово становится истинным, поскольку статический объект теперь инициализирован, поэтому мьютекс не нужно захватывать.
Теперь объявите класс, который вызывает GrabMutex()
в конструкторе и ReleaseMutex(flag)
в деструкторе, сохраняя флаг локально (InitMutexHolder
ниже).
Теперь создайте экземпляр этого класса в вашей обычной функции getSingleton
. Это будет гарантировать, что инициализация синглтона будет мьютексирована с первого раза, и если несколько потоков будут бороться, они выстроятся в мьютекс. Но как только инициализируется синглтон, ready
сбывается, и доступ будет быстрым. Деструктор вызывается магически после выполнения return theSingleton
, что освобождает мьютекс (или ничего не делает, если мьютекс не был взят).
Но основная часть кода в вашей функции theSingleton()
не изменилась, мы добавили только контрольный объект для синглтона и стековый объект в вызове, который управляет безопасностью потока.
Примечание о барьерах записи: поскольку код безопасен всякий раз, когда ready
ложно, и ready
не может сбываться до тех пор, пока объект не будет инициализирован, код в целом безопасен, если предположить, что записи мгновенно видны. Но может быть задержка в отображении ready=true
, так как нет барьера записи после установки true готовности. Однако в течение этой задержки безопасность все еще сохраняется, поскольку ready=false
является консервативным, безопасным случаем.
class InitMutex
{
public:
InitMutex() : ready(false) { }
bool GrabMutex()
{
if (!ready)
{
mutex.Grab();
return true;
}
else
{
return false;
}
}
void ReleaseMutex(bool flagFromGrabMutex)
{
if (flagFromGrabMutex)
{
mutex.Release();
ready = true;
}
}
Mutex mutex;
bool ready;
};
class InitMutexHolder
{
public:
InitMutexHolder(InitMutex & m)
: initMutex(m)
{
inMutex = initMutex.GrabMutex();
}
~InitMutexHolder()
{
initMutex.ReleaseMutex(inMutex);
}
private:
bool inMutex;
InitMutex & initMutex;
};
static InitMutex singletonMutex;
static Singleton & getSingleton()
{
InitMutexHolder mutexHolder(singletonMutex);
{
static Singleton theSingleton;
return theSingleton;
}
}