Безопасен ли этот способ создания статического потока потока? - PullRequest
6 голосов
/ 18 декабря 2010

У меня есть следующий пример кода C ++:

class Factory
{
public:
    static Factory& createInstance()
    {
        static Factory fac;
        return fac;
    }

private:
    Factory() 
    {
        //Does something non-trivial
    }
};

Предположим, что createInstance вызывается двумя потоками одновременно. Так будет ли полученный объект правильно создан? Что происходит, если второй поток входит в вызов createInstance, когда первый поток находится в конструкторе Factory?

Ответы [ 4 ]

7 голосов
/ 18 декабря 2010

C ++ 11 и выше: локальное статическое создание является поточно-ориентированным.

Стандарт гарантирует, что:

  • Создание синхронизировано.
  • Если создание выдает исключение, в следующий раз, когда поток выполнения пройдет точку определения переменной, попытка создания будет предпринята снова.

Как правило, это выполняется с двойной проверкой:

  • сначала проверяется локальный флаг потока, и если он установлен, то к переменной обращаются.
  • , если еще не установлен, то берется более дорогой синхронизированный путь, и еслиПосле этого создается переменная, устанавливается локальный флаг потока.

C ++ 03 и C ++ 98: стандарт не знает потока.

Нет никаких потоков в том, что касается Стандарта, и поэтому в Стандарте нет положений, касающихся синхронизации между потоками.

Однако некоторые компиляторы реализуют на больше , чем стандартные мандатыЛибо в течением расширений или предоставляя более строгие гарантии, так что обратите внимание на интересующие вас компиляторы. Если они хорошего качества, есть вероятность, что они это гарантируют.

Наконец, это может быть необязательно дляэто должно быть потокобезопасным.Если вы вызовете этот метод за до создания какого-либо потока, то вы убедитесь, что он будет правильно инициализирован до того, как в игру вступит реальная многопоточность, и вы аккуратно обойдете проблему.

2 голосов
/ 18 декабря 2010

Глядя на эту страницу , я бы сказал, что это не потоково-безопасный, потому что конструктор может быть вызван несколько раз, прежде чем переменная будет окончательно назначена. InterlockedCompareExchange() может потребоваться, когда вы создаете локальную копию переменной, а затем атомно назначаете указатель на статическое поле через блокированную функцию, если статическая переменная равна нулю.

0 голосов
/ 12 ноября 2011

Сама копия (первый вызов) сама по себе threadsafe .

Однако последующий доступ не будет , как правило.Например, предположим, что после создания экземпляра один поток вызывает изменяемый метод Factory, а другой вызывает некоторый метод доступа в Factory, тогда у вас возникнут проблемы.

Например, если ваша фабрика ведет подсчет количества экземпляровсозданный, у вас будут проблемы без какого-либо мьютекса вокруг этой переменной.

Однако, если Factory действительно класс без состояния (без переменных-членов), то у вас все будет в порядке.

0 голосов
/ 19 декабря 2010

Конечно, это потокобезопасно! Если вы не являетесь полными безумцами и порождаете потоки от конструкторов статических объектов, у вас не будет никаких потоков до тех пор, пока не будет вызван main (), а метод createInstance просто возвращает ссылку на уже созданный объект, это никак не может потерпеть поражение. ISO C ++ гарантирует, что объект будет создан до первого использования после вызова main (): нет никакой гарантии, что это будет до вызова main, но это должно быть до первого использования, и поэтому все системы будут выполнять инициализацию до main () называется. Конечно, ISO C ++ не определяет поведение при наличии потоков или динамической загрузки, но все компиляторы для компьютеров уровня хоста предоставляют эту поддержку и будут пытаться сохранить семантику, указанную для однопоточного статически связанного кода, где это возможно.

...