да нихера не подходит, не важно какой объект ты для синхронизации используешь, если ты его объявил статиком - это уже не рабочий вариант, т.к. статик переменная в момент обращения к ней из метода Instance может быть еще не инициализирована.
При использовании именованного мьютекса, можно обойтись без статика, создавать мьютекс по имени прямо внутри метода Instance(). Таким образом мы не используем статик, а синхронизацию выполняет система.
Схуяли? Если Instance - статик, а он обязан быть статик - то все будет хорошо.
потому что кроме статика с объектом синхронизации класса MySinglton, в программе может быть еще масса других статик переменных. Инициализируются они все до main(). А теперь представь что будет, если до того как инициализируется MySingleton::s_syncRoot, произойдет инициализация другой статической переменной, которая во время инициализации обратится к MySingleton::Instance()?
А будет то что при вызове Instance() переменная MySingleton::s_syncRoot окажется еще не инициализированной, поэтому попытка использовать ее для синхронизации закончится крешем.
Дизайн значит хуевый если код полагается на порядок инициализации статиков. Не должно быть такой русской рулетки.
ну так в этом задача ТС-а и заключается - избавиться от этой русской рулетки. Есть идеи как сделать дизайн синглтона безопасным в этом отношении, без использования системных мьютексов?
Ведь порядок вызова статических инитиалайзеров не гарантирован компилятором.
Ну там прагмы завсегда есть. Но это пиздос еще тот.
Чтоб избавиться от русской рулетки - надо дизайнить нормально, а не костыли для одноглазых потом изобретать.
оно-то понятно, только в жизни редко так бывает чтобы дали денег все передизайнить, поэтому приходится решать проблемы в том что есть...
Кстати volatile в С++ не обспечивает атомарность, поэтому полагаться на него в многопоточной среде в корне неправильно.
Ты как всегда все неправильно понимешьЯ так понимаю, лугал написал какую-то хуйню и теперь хочет нашей помощи чтоб его зарплаты не лишили
Я рассматривал различные варианты, в том числе и с именованным мьютексом.Ибо вопрос реально слишком простой чтобы
а) без помощи гугла не разобраться самому
б) не сделать тупо в лоб с именоваными мутехами.
Вообщето код из старт поста это есть искомый синглетон в С++ 11, так как стандартом языка в данном случае гарантируеться полностью потокобезопасная инициализация статистеской внутренней переменной в момент первого вызова функции.с) в стартпосле не писать код заведомо нихуя вообще не синглетона, а какой-то неведомой хуйни
Так в гугли хоть много хоть немного особо данный кейс и не решен. DCL эфективен в многопоточности, но с глобал скопом проблемы, из за инициализации объекта синхронизации.д) все-таки погуглить немного
Полная постоновка задачи, если тебе интересно звучит так:
В разрабатываемой коммерческой библиотеки сделать базовый класс синглтон, требующий минимального дополнительного кода, для превращения любого класса, который его наследует в синглтон.
Синглтон должен быть потоко и контекстно безопасен.
Я рассматривал различные варианты, в том числе и с именованным мьютексом.
В случае с именнованным мьютексом непонятно кто должен закрывать хендлы, это раз. Два - дорого по времени.
Вообщето код из старт поста это есть искомый синглетон в С++ 11, так как стандартом языка в данном случае гарантируеться полностью потокобезопасная инициализация статистеской внутренней переменной в момент первого вызова функции.
Так в гугли хоть много хоть немного особо данный кейс и не решен. DCL эфективен в многопоточности, но с глобал скопом проблемы, из за инициализации объекта синхронизации.
class Singleton
{
static Singleton* volatile _instance;
protected:
..........................
public:
..........................
static Singleton* Instance()
{
static LONG volatile lock = 0;
if (!_instance)
{
if (!InterlockedCompareExchange(&lock, 1, 0))
{
_instance = new Singleton();
}
else
{
while (!_instance)
{
Sleep(0);
}
}
}
return _instance;
}
..........................
};
Так. Объясните мне, дураку, почему, например, такая конструкция не будет удовлетворять условиям?
Код:class Singleton { static Singleton* volatile _instance; protected: .......................... public: .......................... static Singleton* Instance() { static LONG volatile lock = 0; if (!_instance) { if (!InterlockedCompareExchange(&lock, 1, 0)) { _instance = new Singleton(); } else { while (!_instance) { Sleep(0); } } } return _instance; } .......................... };
Это просто пример реализации.
Да легко. теоретически с момента интелока до момента создания - другой поток может успеть тот же путь пройти.
Интерлок лочит только переменную, а не кусок кода.
Интерлок лочит шину памяти, так что другой поток этот путь при всём желании не успеет пройти. Плюс мемори барьер, как бонус для мультипроцессорности.
Другое дело, что значение _instance должно быть инициализировано видом:
Singleton* Singleton::_instance(NULL);
И вот тут лично у меня вопрос: эта инициализация относится к этапу инициализации константных объектов или этапу инициализации статических и глобальных переменных.
Если к этапу константных, то всё будет хорошо, а вот нет, то в теории мы можем заиметь рандомный ненулевой адрес указателя, что провалит все тесты. Но, о чудо, в такой ситуации мы можем проверять не _instance на ноль, а lock на ноль (точнее не на ноль, а на... два, но это уже детали).
Лочит на момент compare exchange. И все. На этом лок заканчивается.
Этап второй. Лок проверять не выйдет, он не гарантирует _instance ничего.
Ну так больше и не надо.
В худшем случае при переключении контекста после выполнения интерлок-инструкции первым потоком, второй поток будет стоять перед выполнением интерлок-инструкции, которую он успешно провалит и перейдёт в цикл со слипом.
Проверка лок гарантирует, что при состоянии:
a) lock = 0, _instance не инициализирован, блокировка не выполнена.
b) lock = 1, _instance не инициализирован, блокировка выполняется.
c) lock = 2, _instance инициализирован.
Код написать?)
Видимо ты прав.
А покажи, а то я что-то туплю.
enum SingletonInitState {sisNotInitialized = -1, sisInitializing = 0, sisInitialized = 1};
class Singleton
{
private:
static Singleton* _instance;
protected:
Singleton() {}
Singleton(Singleton const&);
void operator = (Singleton const&);
public:
static Singleton* Instance()
{
static LONG volatile lock = sisNotInitialized;
switch (lock)
{
case sisNotInitialized:
{
if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized)
{
_instance = new Singleton();
lock = sisInitialized;
break;
}
}
case sisInitializing:
{
while (!_instance)
{
Sleep(0);
}
break;
}
}
return _instance;
}
virtual ~Singleton() {}
};
Я на самом деле тоже теоритезирую, потомушо сам как-то с этой ситуацией на приктике не сталкивался
С удовольствием. Сейчас только обеденную партию в Анреал закончим и напишу
Шото вроде такого:
Код:enum SingletonInitState {sisNotInitialized = -1, sisInitializing = 0, sisInitialized = 1}; class Singleton { private: static Singleton* _instance; protected: Singleton() {} Singleton(Singleton const&); void operator = (Singleton const&); public: static Singleton* Instance() { static LONG volatile lock = sisNotInitialized; switch (lock) { case sisNotInitialized: { if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized) { _instance = new Singleton(); lock = sisInitialized; break; } } case sisInitializing: { while (!_instance) { Sleep(0); } break; } } return _instance; } virtual ~Singleton() {} };
Я на самом деле тоже теоритезирую, потомушо сам как-то с этой ситуацией на приктике не сталкивался
С удовольствием. Сейчас только обеденную партию в Анреал закончим и напишу
Шото вроде такого:
Код:enum SingletonInitState {sisNotInitialized = -1, sisInitializing = 0, sisInitialized = 1}; class Singleton { private: static Singleton* _instance; protected: Singleton() {} Singleton(Singleton const&); void operator = (Singleton const&); public: static Singleton* Instance() { static LONG volatile lock = sisNotInitialized; switch (lock) { case sisNotInitialized: { if (InterlockedCompareExchange(&lock, sisNotInitialized, sisInitializing) == sisNotInitialized) { _instance = new Singleton(); lock = sisInitialized; break; } } case sisInitializing: { while (!_instance) { Sleep(0); } break; } } return _instance; } virtual ~Singleton() {} };
Идея с интерлоком
Но теоретически у нас может быть два static LONG volatile lock = sisNotInitialized, а следовательно два инстанса синглетона, один из которых "потеряеться", если два потока одновременно войдут в эту точку.
Сам понял что сказал?