Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 07:25
Оценка:
Доброго времени суток всем нам!

Понадобился мне тут сабж... в boost'е ничего подобного, к моему глубочайшему удивлению, не обнаружилось; гуглевание по словам "C++ singleton thread safe" выдало лишь массу ссылок на "общие слова", по прочтении которых наваялось вот такое:

// interface

template <class _Class_t>
class Singleton
{
// construction/destruction
protected:
    Singleton(void);
    ~Singleton(void);

// copying/assignment - disabled and thus not implemented
private:
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

// object creator/accessor
public:
    static _Class_t& GetObject(void);

// attributes
private:
    class Locker: public CRITICAL_SECTION
    {
        friend class Singleton<_Class_t>;
        Locker(void);
        ~Locker(void);
        void Enter(void);
        void Leave(void);
    };
    static Locker m_objectLocker;
};

// Singleton<> implementation

template <class _Class_t>
inline Singleton<_Class_t>::Singleton(void)
{
}

template <class _Class_t>
inline Singleton<_Class_t>::~Singleton(void)
{
}

template <class _Class_t>
_Class_t& Singleton<_Class_t>::GetObject(void)
{
    m_objectLocker.Enter();
    static _Class_t* pObject;
    if (pObject == NULL)
    {
        pObject = new _Class_t();
    }
    m_objectLocker.Leave();
    return (*pObject);
}

template <class _Class_t>
typename Singleton<_Class_t>::Locker Singleton<_Class_t>::m_objectLocker;

// Singleton<>::Locker implementation

template <class _Class_t>
inline Singleton<_Class_t>::Locker::Locker(void)
{
    ::InitializeCriticalSection(this);
}

template <class _Class_t>
inline Singleton<_Class_t>::Locker::~Locker(void)
{
    ::DeleteCriticalSection(this);
}

template <class _Class_t>
inline void Singleton<_Class_t>::Locker::Enter(void)
{
    ::EnterCriticalSection(this);
}

template <class _Class_t>
inline void Singleton<_Class_t>::Locker::Leave(void)
{
    ::LeaveCriticalSection(this);
}

Метод использования очевиден:

class MyOneAndOnly: public Singleton<MyOneAndOnly> { ... };

MyOneAndOnly::GetObject().some_method();

Теперь собственно проблемы/вопросы:

  1. Обращение к методам MyOneAndOnly потенциально возможно из конструкторов/деструкторов глобальных объектов (почему pObject и создается в куче). С первыи проблем, как я понимаю не возникнет, так как при первом же общении произойдет вызов new _Class_t. А вот деструкторами все хуже, потому как никаких гарантий, что уже не был разрушен Locker, у меня нет. Это можно как-то преодолеть?

  2. Очевидно, что когда-то нужно вызвать delete pObject — но как это сделать "безопасно" (то есть, с гарантией, что все глобальные объекты уже разрушены)? Копать в сторону atexit()?

  3. Я правильно понимаю, что реализовать singleton для класса, который не имеет конструктора по умолчанию, невозможно?

P.S.
В ожидании ответов пошел читать Паттерн Singleton (Одиночка). Примеры использования
Автор(ы): Дмитрий Федоров
Дата: 14.11.2002
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re: Потоко-бесопасный синглетон
От: DigitalGuru Россия http://svetlyak.ru
Дата: 24.05.06 07:41
Оценка: 1 (1) +1 :))
Здравствуйте, SchweinDeBurg, Вы много чего писали.

Почитать Александреску. Поставить велосипед в сарай. Использовать Loki.
Re[2]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 07:58
Оценка: -1
Здравствуйте, DigitalGuru, Вы писали:

DG>Почитать Александреску. Поставить велосипед в сарай. Использовать Loki.


Отпадает — мне для "промышленного" кода надо. Loki, в отличие от boost'а, я таковым не воспринимаю.

P.S.
Сорри за взаимную категоричность.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re: Потоко-бесопасный синглетон
От: Константин Л. Франция  
Дата: 24.05.06 08:48
Оценка: 14 (1)
Здравствуйте, SchweinDeBurg, Вы писали:

[]

SDB>// attributes

SDB>private:
SDB> class Locker: public CRITICAL_SECTION
SDB> {
SDB> friend class Singleton<_Class_t>;
SDB> //public://???
SDB> Locker(void);
SDB> ~Locker(void);
SDB> void Enter(void);
SDB> void Leave(void);
SDB> };
SDB> static Locker m_objectLocker;
SDB>};

[]

SDB>MyOneAndOnly::GetObject().some_method();

SDB>[/ccode]
SDB>Теперь собственно проблемы/вопросы:

SDB>

    SDB>
  1. Обращение к методам MyOneAndOnly потенциально возможно из конструкторов/деструкторов глобальных объектов (почему pObject и создается в куче). С первыи проблем, как я понимаю не возникнет, так как при первом же общении произойдет вызов new _Class_t. А вот деструкторами все хуже, потому как никаких гарантий, что уже не был разрушен Locker, у меня нет. Это можно как-то преодолеть?

    SDB>
  2. Очевидно, что когда-то нужно вызвать delete pObject — но как это сделать "безопасно" (то есть, с гарантией, что все глобальные объекты уже разрушены)? Копать в сторону atexit()?

    SDB>
  3. Я правильно понимаю, что реализовать singleton для класса, который не имеет конструктора по умолчанию, невозможно?
    SDB>

SDB>P.S.

SDB>В ожидании ответов пошел читать Паттерн Singleton (Одиночка). Примеры использования
Автор(ы): Дмитрий Федоров
Дата: 14.11.2002


Почему Loki не воспринимаешь?

1) Зачем писать свой Locker, когда есть ATL::CComAutoCriticalSection, ATL::CComCritSecLock? Или нет возможности их юзать?
2) Зачем у Locker'а private конструктор? Класс же и так private? Из-за этого тебе пришлось тянуть friend. Сделай конструктор/десруктор public
3) Есть идея создавать CS в куче и вообще не удалять ни твой объект, ни CS. При грохе процесса все почистится. Но это зависит от "сложности" деструктора MyOneAndOnly
4) Может все-таки Loki + напильник?
Re[2]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 09:22
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Почему Loki не воспринимаешь?


Сложно сказать... для меня это больше "академический эксперимент" по проверке и демонстрации возможностей C++ и каких-то концепций, чем "боевой" продукт.

КЛ>1) Зачем писать свой Locker, когда есть ATL::CComAutoCriticalSection, ATL::CComCritSecLock? Или нет возможности их юзать?


Нет, нужна максимальная независимость от любых библиотек (исключая STL).

КЛ>2) Зачем у Locker'а private конструктор? Класс же и так private? Из-за этого тебе пришлось тянуть friend. Сделай конструктор/десруктор public


Согласен.

КЛ>3) Есть идея создавать CS в куче и вообще не удалять ни твой объект, ни CS. При грохе процесса все почистится.


За такой подход у нас на работе рихтуют напильником... сначала руки, потом — голову. И очень правильно ИМХО делают.

КЛ>Но это зависит от "сложности" деструктора MyOneAndOnly


Вот именно.

КЛ>4) Может все-таки Loki + напильник?


Нет уж, увольте. Я тут нагугли-таки пару полезных ссылочек по теме со вполне читабельным кодом:

Singleton.h

Context Singletons &mdash; context-specific shared objects in C++

Как я и предполагал, через atexit() проблема решается.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[3]: Потоко-бесопасный синглетон
От: DigitalGuru Россия http://svetlyak.ru
Дата: 24.05.06 09:30
Оценка: +1
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Как я и предполагал, через atexit() проблема решается.


Конечно! У Александреску именно так и написано
Re[4]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 09:34
Оценка:
Здравствуйте, DigitalGuru, Вы писали:

DG>Конечно! У Александреску именно так и написано


"Great minds think alike..." Но код из Context Singletons — context-specific shared objects in C++ читается значительно приятнее.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[3]: Потоко-бесопасный синглетон
От: Константин Л. Франция  
Дата: 24.05.06 09:45
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Здравствуйте, Константин Л., Вы писали:


КЛ>>Почему Loki не воспринимаешь?


SDB>Сложно сказать... для меня это больше "академический эксперимент" по проверке и демонстрации возможностей C++ и каких-то концепций, чем "боевой" продукт.


КЛ>>1) Зачем писать свой Locker, когда есть ATL::CComAutoCriticalSection, ATL::CComCritSecLock? Или нет возможности их юзать?


SDB>Нет, нужна максимальная независимость от любых библиотек (исключая STL).


КЛ>>2) Зачем у Locker'а private конструктор? Класс же и так private? Из-за этого тебе пришлось тянуть friend. Сделай конструктор/десруктор public


SDB>Согласен.


КЛ>>3) Есть идея создавать CS в куче и вообще не удалять ни твой объект, ни CS. При грохе процесса все почистится.


SDB>За такой подход у нас на работе рихтуют напильником... сначала руки, потом — голову. И очень правильно ИМХО делают.


ИМХО тоже, просто предложил как один из "возможных" вариантов

КЛ>>Но это зависит от "сложности" деструктора MyOneAndOnly


SDB>Вот именно.


КЛ>>4) Может все-таки Loki + напильник?


SDB>Нет уж, увольте. Я тут нагугли-таки пару полезных ссылочек по теме со вполне читабельным кодом:


SDB>Singleton.h


Есть сомнения по поводу необходимости наследования от SingletonBase. Этот класс является аналогом boost::noncopyable и ничего не дает кроме невозможности копирования.

Так что необходимость наследования от него считаю лишней

SDB>Context Singletons &mdash; context-specific shared objects in C++


SDB>Как я и предполагал, через atexit() проблема решается.
Re: Потоко-бесопасный синглетон
От: Ivan Россия www.rsdn.ru
Дата: 24.05.06 09:47
Оценка: 14 (1)
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Понадобился мне тут сабж... в boost'е ничего подобного, к моему глубочайшему удивлению, не обнаружилось;


в boost'е есть синглтон — но в виде детали реализации библиотеки pool — boost\pool\detail\singleton.hpp
идея потокобезопасности проста и эффективна — используется синглтон Майерса + instance() первый раз вызывается в конструкторе глобальной переменной, это происходит до входа в main и, следовательно, потокобезопасно.

код примерно такой
template<class T> class singleton : public T
{
private:
...
   struct creator
   {
       creator()
       {
          singleton<T>::instance();
       }  
   }
   static creator s_singleton_creator;

public:
   static T& instance()
   {
      static T inst;
      return inst;
   }
};

template<class T> typename singleton<T>::creator singleton<T>::s_singleton_creator;
Re[4]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 09:49
Оценка: 1 (1)
Здравствуйте, Константин Л., Вы писали:

КЛ>ИМХО тоже, просто предложил как один из "возможных" вариантов


Для меня он не возможен, даже в кавычках.

КЛ>Есть сомнения по поводу необходимости наследования от SingletonBase. Этот класс является аналогом boost::noncopyable и ничего не дает кроме невозможности копирования.


Угу.

КЛ>Так что необходимость наследования от него считаю лишней


Ну, это уже частности... при наличии рашпиля.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[2]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 10:03
Оценка:
Здравствуйте, Ivan, Вы писали:

I>в boost'е есть синглтон — но в виде детали реализации библиотеки pool — boost\pool\detail\singleton.hpp

I>идея потокобезопасности проста и эффективна — используется синглтон Майерса + instance() первый раз вызывается в конструкторе глобальной переменной, это происходит до входа в main и, следовательно, потокобезопасно.

Засмотрел. Все бы хорошо, но:

1.

// The following helper classes are placeholders for a generic "singleton"
// class. The classes below support usage of singletons, including use in
// program startup/shutdown code, AS LONG AS there is only one thread
// running before main() begins, and only one thread running after main()
// exits
.


Будучи параноиком, выделенным смущен.

2.

// Furthermore, since the instance() function contains the object, instead
// of the singleton_default class containing a static instance of the
// object, that object is guaranteed to be constructed (at the latest) in
// the first call to instance(). This permits calls to instance() from
// static code, even if that code is called before the file-scope objects
// in this file have been initialized.


Увы, но разрушение такого синглетона после всех глобальных объектов, которые им пользовались, не гарантируется.

Так что буду сейчас точить под себя Феникса, описанного в Context Singletons — context-specific shared objects in C++. Но за инфу все равно спасибо.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re: Потоко-бесопасный синглетон
От: Conr Россия  
Дата: 24.05.06 10:06
Оценка: 14 (1)
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Доброго времени суток всем нам!


SDB>Понадобился мне тут сабж... в boost'е ничего подобного, к моему глубочайшему удивлению, не обнаружилось; гуглевание по словам "C++ singleton thread safe" выдало лишь массу ссылок на "общие слова", по прочтении которых наваялось вот такое:

Так на RSDN же и была интересная тема thread safe singlton &mdash; возможно ли такое в принципе
Автор: Andrew S
Дата: 06.02.04


хъ
Re: Потоко-бесопасный синглетон
От: Андрей Коростелев Голландия http://www.korostelev.net/
Дата: 24.05.06 10:10
Оценка: 14 (1)
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Доброго времени суток всем нам!


SDB>Понадобился мне тут сабж... в boost'е ничего подобного, к моему глубочайшему удивлению, не обнаружилось; гуглевание по словам "C++ singleton thread safe" выдало лишь массу ссылок на "общие слова", по прочтении которых наваялось вот такое:


Кроме всего прочего, советую почитать эту
Автор: LuciferMoscow
Дата: 21.10.05
ветку
-- Андрей
Re[2]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 10:13
Оценка:
Здравствуйте, Conr, Вы писали:

C>Так на RSDN же и была интересная тема thread safe singlton &mdash; возможно ли такое в принципе
Автор: Andrew S
Дата: 06.02.04


Ну, вот так всегда: спросишь что-нибудь на RSDN — пошлют в Гугль, а если первым делом скажешь, что в Гугле уже был — "а чего ты на RSDN не посмотрел?.."

Спасибо, Вит, занес ссылку в избранное, почитаю на досуге.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[5]: Потоко-бесопасный синглетон
От: Аноним  
Дата: 24.05.06 10:29
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Здравствуйте, DigitalGuru, Вы писали:


DG>>Конечно! У Александреску именно так и написано


SDB>"Great minds think alike..." Но код из Context Singletons — context-specific shared objects in C++ читается значительно приятнее.


Full disclosure: phoenix is based partly on MC++D's code. In particular, i've stolen Andrei's trick of matching atexit(), manual dtor invokation and the placement new operator .


Еще позабавило имя переменной "myers" .

А что касается реализации Singlton'а, то, мне лично, приятнее вот это:

class my_singleton_t {
...
};

extern my_singleton_t my_singleton;
Re[3]: Потоко-бесопасный синглетон
От: Аноним  
Дата: 24.05.06 10:36
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Здравствуйте, Ivan, Вы писали:


[snip]

SDB>1.

SDB>

SDB>// The following helper classes are placeholders for a generic "singleton"
SDB>// class. The classes below support usage of singletons, including use in
SDB>// program startup/shutdown code, AS LONG AS there is only one thread
SDB>// running before main() begins, and only one thread running after main()
SDB>// exits
.


SDB>Будучи параноиком, выделенным смущен.


А у вас часто создаются нити до входа в main? Правда, очень интересно где это может понадобиться.

[snip]

SDB>Увы, но разрушение такого синглетона после всех глобальных объектов, которые им пользовались, не гарантируется.


здесь
Автор: Erop
Дата: 20.04.06
интересное, ИМХО, мнение по этому поводу.

[snip]
Re[4]: Потоко-бесопасный синглетон
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 24.05.06 10:48
Оценка:
Здравствуйте, <Аноним>, Вы писали:

SDB>>Будучи параноиком, выделенным смущен.

А>А у вас часто создаются нити до входа в main? Правда, очень интересно где это может понадобиться.

См. выделенное. Мне крайне не хочется копаться в исходниках CRT очередного компилятора, чтобы убедиться, что до входа в main() мы имеем ровно один поток.

А>здесь
Автор: Erop
Дата: 20.04.06
интересное, ИМХО, мнение по этому поводу.


ОК, засмотрю.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[3]: Потоко-бесопасный синглетон
От: Ivan Россия www.rsdn.ru
Дата: 24.05.06 11:23
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Засмотрел. Все бы хорошо, но:


SDB>1.

SDB>

SDB>// AS LONG AS there is only one thread
SDB>// running before main() begins, and only one thread running after main()
SDB>// exits
.


если речь идет о Dll модуле, то вызов DllMain происходит внутри спецальной секции LoaderLock и создание других потоков в этот момент будет приостановлено до завершения работы DllMain. В exe модуле технически завести несколько потоков можно — в конструктторах глобальных переменных, но я с таким не встречался.
если представить, что кто-то создает потоки в конструкторах глобальных переменных, то в это время еще не завершилась инициализация CRT в главном потоке — это может создать проблемы при инициализации CRT в порожденных потоках. и второй момент — при обращении из этих потоков к глобальным переменным — можно получить доступ к переменным, которые еще не успели сконструироваться в главном потоке.

SDB>Увы, но разрушение такого синглетона после всех глобальных объектов, которые им пользовались, не гарантируется.

да, здесь похоже по стандарту UB при обращении после его разрушения, на практике для Visual C++ — он 'возродится'
Re[3]: Потоко-бесопасный синглетон
От: Conr Россия  
Дата: 24.05.06 12:25
Оценка: :)
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Ну, вот так всегда: спросишь что-нибудь на RSDN — пошлют в Гугль, а если первым делом скажешь, что в Гугле уже был — "а чего ты на RSDN не посмотрел?.."

Хочешь я тебе тулзу дам, которая ищет одновременно и там, и там, объединяя результаты вместе? Только я её пока в гугль-группах и на codeproject искать не научил, но это в планах есть...

SDB>Спасибо, Вит, занес ссылку в избранное, почитаю на досуге.
Re: Потоко-бесопасный синглетон
От: Programmierer AG  
Дата: 24.05.06 13:40
Оценка: 15 (2)
Здравствуйте, SchweinDeBurg, Вы писали:

Смущает пара моментов, вроде в ветке не упоминались:
SDB>
SDB>    static Locker m_objectLocker; // 1) инициализируется в runtime - обращение
                                      // к GetObject из других единиц трансляции
                                      //  может обломиться

SDB>template <class _Class_t>
SDB>_Class_t& Singleton<_Class_t>::GetObject(void)
SDB>{
SDB>    m_objectLocker.Enter();
SDB>    static _Class_t* pObject;
SDB>    if (pObject == NULL)
SDB>    {
SDB>        pObject = new _Class_t();// 2) маловероятно, но может бросить исключение,
SDB>    }
SDB>    m_objectLocker.Leave(); // ...и сюда мы не попадаем
SDB>    return (*pObject);
SDB>}

SDB>

SDB>Теперь собственно проблемы/вопросы:

SDB>[list=1]

SDB>
  • Обращение к методам MyOneAndOnly потенциально возможно из конструкторов/деструкторов глобальных объектов (почему pObject и создается в куче). С первыи проблем, как я понимаю не возникнет,
    См. 1), могут возникнуть.
  • Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.