Как я могу избежать потока статических данных init Fiasco потокобезопасным способом? - PullRequest
1 голос
/ 25 июня 2011

Учтите это:

class A {
public:
    A(const char* s) { /*...*/ };
    static const A& get_default() { /* avoid static data memember init fiasco */
        static const A& a = A("default"); /* ..but not thread-safe */
        return a;
    }
};

Как я могу сделать get_default() потокобезопасным?Или как я могу найти способ получить A::a, избегая как фиаско инициализации члена данных, так и параллелизма.Я предпочитаю не использовать никаких замков.

1 Ответ

1 голос
/ 25 июня 2011

Далее будет использован тот факт, что инициализация до вызова main() будет выполняться в одном потоке, и указатель pInstance должен быть инициализирован нулем, прежде чем произойдет динамическая инициализация статических объектов (3.6.2«Инициализация нелокальных объектов»).

class A {
public:
    A(const char* s) { /*...*/ };

    static const A& get_default() { 
        if (!pInstance) {
            static const A default_instance("default");
            pInstance = &default_instance;
        }
        return *pInstance;
    }

private:
    static A const* pInstance;
};

A const* A::pInstance = &A::get_default();

Таким образом, pInstance будет иметь значение NULL при первом вызове get_default() - независимо от того, вызван ли этот вызов инициализацией A::pInstance.Это заставит get_default() инициализировать его (pInstance будет «без необходимости» перезаписываться тем же значением при запуске инициализатора - но это будет в одном потоке инициализации, поэтому проблем с потоками не будет).И чтобы было ясно, инициализатор для pInstance заставит get_default() вызываться в потоке инициализации, даже если ничего не происходит.

Последующие вызовы get_default() не изменят pInstance, поэтомуэто потокобезопасно.

Обратите внимание, что проще:

class A {
public:
    A(const char* s) { /*...*/ };

    static const A& get_default() { 
        static const A& a = A("default"); /* ..but not thread-safe */
        return a;
    }
};

namespace {
    A const& trigger = A::get_default();
}

По той же причине должно быть также потокобезопасно, но у него есть пара областей, которые делают меня немного менее уверенным, что он не будетимеют некоторые потенциальные недостатки:

  • , если набор инструментов определяет, что trigger не используется в другом месте (и это применимо, даже если мы вытащим его из анонимного пространства имен), я бы опасался, что это можетудалить из образа программы.Я не уверен в том, что может дать стандарт по предотвращению этой оптимизации, особенно если class A живет в библиотеке.
  • предположительно, компилятор просто проверяет скрытый статический флаг, чтобы определить, инициализирована ли локальнаяstatic переменная в get_default() была запущена ранее.Однако на самом деле нет никаких обещаний относительно того, какой механизм используется (я не изучал, что C ++ 0x может сказать по этому поводу).В первом примере потокобезопасная проверка после вызова init get_default() прямо здесь - гарантировано.
...