Потокобезопасность Meyers Singleton с C++-98

В настоящее время у меня есть эта реализация синглтона Мейера:

class ClassA
{
public:
    static ClassA& GetInstance()
    {                   
        static ClassA instance;     
        return instance;
    }   

private:
    ClassA::ClassA() {};

    // avoid copying singleton
    ClassA(ClassA const&);
    void operator = (ClassA const&);
};

Теперь мне нужны некоторые улучшения, чтобы сделать этот код потокобезопасным в C++-98 и VS-2008?!

Спасибо!

P.S. Что непонятно? Вы видите теги visual-studio-2008 и c++-98 ->, значит, целевая ОС — Windows! Я также не понимаю, почему за меня проголосовали только некоторые люди, которым вообще не нравится Синглтон!


person leon22    schedule 29.07.2013    source источник
comment
Самое простое улучшение — не использовать синглтон.   -  person Mike Seymour    schedule 29.07.2013
comment
В чем именно заключается ваш вопрос? Хотите знать, какие механизмы синхронизации использовать? Поскольку в стандарте С++ 98 нет потоков, это зависит от ОС и используемой библиотеки потоков.   -  person Hulk    schedule 29.07.2013
comment
-1 за использование синглтона вообще, и vtc, потому что вы 1) не задавали реальных вопросов и 2) кажется, думаете, что переполнение стека - это волшебная машина для генерации кода.   -  person John Dibling    schedule 29.07.2013
comment
@MikeSeymour Не зная, для чего он его использует, вы не можете этого сказать. Есть определенные вещи, для которых синглтон является подходящим решением.   -  person James Kanze    schedule 29.07.2013
comment
@JohnDibling Вопрос очевиден (и имеет простой ответ), и он может быть полностью оправдан с помощью синглтона, поскольку бывают случаи, когда синглтон является лучшим решением. (Избегание синглетонов в принципе можно классифицировать как антишаблон.)   -  person James Kanze    schedule 29.07.2013
comment
Обычно я не голосую, но голосую за этот вопрос, потому что это хороший вопрос, за который неоправданно проголосовали.   -  person James Kanze    schedule 29.07.2013
comment
@JamesKanze: я могу и скажу, что синглтоны никогда не найдут подходящего применения (хотя, конечно, вы можете не согласиться); вдвойне, если вы пытаетесь модифицировать потокобезопасность до C++98. Насколько мне известно, невозможно реализовать потокобезопасный синглтон без гарантий C++11 в отношении локальных статических объектов.   -  person Mike Seymour    schedule 29.07.2013
comment
@MikeSeymour, я полагаю, вы можете сказать что угодно, но подходящие варианты использования указывались не раз, и никто еще не представил никаких аргументов против них в целом. (Конечно, есть веские причины не использовать их слишком часто. Но то же самое можно сказать и о любой расширенной языковой функции.)   -  person James Kanze    schedule 29.07.2013
comment
Что непонятно? Вы видите теги visual-studio-2008 и c++-98 ->, значит, целевая ОС — Windows! Я также не понимаю, почему за меня проголосовали только некоторые люди, которым вообще не нравится Синглтон!   -  person leon22    schedule 30.07.2013


Ответы (1)


Синглтон Мейера — не лучшее решение в целом, особенно в многопоточной среде. Более общий способ реализации синглтона:

class ClassA
{
    static ClassA* ourInstance;
    //  ctor's, etc.
public:
    static ClassA& instance();
};

и в исходном файле:

ClassA* ClassA::ourInstance = &instance();

// This can be in any source file.
ClassA&
ClassA::instance()
{
    if ( ourInstance == NULL ) {
        ourInstance = new ClassA;
    }
    return *ourInstance;
}

Это потокобезопасный если потоки не создаются перед входом в main (что должно иметь место) и он не загружается динамически (что также должно иметь место, если объект должен быть уникальным и доступным из конструкторы статических объектов, то он должен быть их при запуске статических конструкторов). Это также имеет то преимущество, что позволяет избежать проблем с порядком уничтожения.

person James Kanze    schedule 29.07.2013
comment
Разве ваши рассуждения о том, что это потокобезопасно, не работают для исходного кода? - person GManNickG; 29.07.2013
comment
Гарантируется ли инициализация статического члена перед вводом main, даже если он находится в другой единице перевода? Это специально не гарантируется в С++ 11, но С++ 98/03 кажется мне более расплывчатым. - person Mike Seymour; 29.07.2013
comment
@MikeSeymour Официально я не думаю, что это гарантировано даже в C ++ 11. На практике от этого зависит так много кода, что, я думаю, вам не о чем беспокоиться. - person James Kanze; 29.07.2013
comment
@GManNickG Нет. В исходном коде не было никаких гарантий, что ClassA::instance будет вызываться перед вводом main. Мой код делает. - person James Kanze; 29.07.2013
comment
@JamesKanze: это конкретно не гарантируется в C++11; 3.6.2/4 говорит, что реализация определяет, выполняется ли динамическая инициализация нелокальной переменной со статической продолжительностью хранения до первого оператора main. Соответствующее предложение в C++98 относится только к объектам в области видимости пространства имен, но, насколько я понимаю, ничего не говорится о каких-либо других статических переменных. Вы можете не беспокоиться о том, чтобы полагаться на поведение, определяемое реализацией или не специфицированное, но я, безусловно, беспокоюсь. - person Mike Seymour; 29.07.2013
comment
@MikeSeymour Примерно так я и думал. Почему это не гарантировано, я не знаю; все реализации эффективно выполняют статическую инициализацию перед входом в main. Исторически сложилось так, что это более или менее относится к той же категории, что и несмежное представление строки — изначально было желание оставить реализациям дополнительную свободу, но оказалось, что с этой свободой они не могут сделать ничего интересного. - person James Kanze; 29.07.2013
comment
@JamesKanze: это необходимо для поздней загрузки динамических библиотек. Это происходит в Linux; ваша попытка обеспечить безопасность потоков на этой платформе потерпит неудачу, если синглтон находится в общей библиотеке. (Я очень мало знаю о Windows, поэтому не могу комментировать, является ли это допустимым, но не переносимым решением для этого конкретного вопроса) - person Mike Seymour; 29.07.2013
comment
@MikeSeymour Как только вы используете динамическую загрузку, вы оказываетесь в неопределенном поведении в отношении стандарта C ++. И да, синглтон должен быть в том же блоке, что и main; это не работает, если оно находится в общей библиотеке. Но вещи, для которых подходят синглтоны, в любом случае не принадлежат разделяемым библиотекам. Если действительно может и должен быть только один из чего-то, он, вероятно, должен быть там, прежде чем вы загрузите какую-либо разделяемую библиотеку. - person James Kanze; 29.07.2013
comment
@JamesKanze: тогда ответ должен содержать эти предостережения; как бы то ни было, это опасно вводит в заблуждение, подразумевая, что этот тип инициализации, как правило, потокобезопасен. И динамическая загрузка (за счет реализации полных единиц перевода), конечно, не является неопределенным поведением; в стандарте достаточно четко указано, на что вы можете и не можете полагаться (кроме неопределенности, о которой я упоминал в C++98, исправленной в C++11). - person Mike Seymour; 29.07.2013
comment
@ leon22 Какой первый метод? Единственная функция в коде, который я представляю, это ClassA::instance, и она делает то, что должна делать: если указатель равен нулю, она создает новый экземпляр и инициализирует указатель. - person James Kanze; 30.07.2013
comment
@JamesKanze Это была еще одна ошибка (это происходит не из вашего кода)! Спасибо в любом случае! - person leon22; 30.07.2013