2009-06-27 2 views
13

Помню, что статические переменные, объявленные внутри методов, не являются потокобезопасными. (См What about the Meyer's singleton?, как упомянуто Todd Gardner)Неподвижные статические переменные без мьютексинга?

Dog* MyClass::BadMethod() 
{ 
    static Dog dog("Lassie"); 
    return &dog; 
} 

Моя библиотека генерирует C++ код для конечных пользователей, чтобы собрать как часть их применения. Созданный им код должен инициализировать статические переменные в поточно-безопасном межплатформенном режиме. Я хотел бы использовать boost::call_once для mutex инициализацию переменных, но затем конечные пользователи подвергаются зависимости Boost.

Есть ли способ для меня сделать это, не вызывая дополнительных зависимостей от конечных пользователей?

+0

Вы также можете статически ссылаться в библиотеках ускорения на свои собственные, тогда вы можете использовать boost все, что хотите, не опасаясь причинить неудобства своим пользователям. – Ben

+0

Возможный дубликат [Является ли локальная статическая переменная инициализацией потокобезопасной в C++ 11?] (Http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11) –

+1

@LucianAdrianGrijincu, этот вопрос предшествует C++ 11 (он был опубликован в 2009 году), поэтому, хотя ваш вопрос связан с этим, он не является строгим дубликатом. Спасибо за ссылку. – Gili

ответ

10

Вы правы, что статическая инициализация так не поточно (here статья обсуждает то, что компилятор превратит его)

На данный момент нет стандарта, поточно, переносимого способа для инициализации статические синглтоны. Можно использовать двойную проверку блокировки, но вам понадобятся потенциально не переносные библиотеки потоков (см. Обсуждение here).

Вот несколько вариантов, если безопасность потоков является обязательным:

  1. Не ленитесь (загружен): Инициализировать во время статической инициализации. Это может быть проблемой, если другой статический вызов вызывает эту функцию в своем конструкторе, поскольку порядок статической инициализации не определен (см. here).
  2. Используйте импульс (как вы сказали) или Локи
  3. Ролл ваш собственный одноточечно на поддерживаемых платформах (вероятно, следует избегать, если вы не эксперт потоковая)
  4. блокировки мьютекса каждый раз, когда вам нужен доступ. Это может быть очень медленно.

Пример 1:

// in a cpp: 
namespace { 
    Dog dog("Lassie"); 
} 

Dog* MyClass::BadMethod() 
{ 
    return &dog; 
} 

Пример 4:

Dog* MyClass::BadMethod() 
{ 
    static scoped_ptr<Dog> pdog; 
    { 
    Lock l(Mutex); 
    if(!pdog.get()) 
     pdog.reset(new Dog("Lassie")); 
    } 
    return pdog.get(); 
} 
+0

Я закончил тем, что кусал пулю и использовал 'boost :: call_once()' – Gili

+7

этот ответ теперь устарел: http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe- в-c11 – Yankes

2

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

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

Единственная альтернатива - это еще одно ограничение на то, как они могут использовать сгенерированный код (используйте Boost, Win32 threads и т. Д.). По моему мнению, любое из этих решений приемлемо - все в порядке, чтобы генерировать правила, которым они должны следовать.

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

3

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

static Dog dog("Lassie"); 
Dog* MyClass::BadMethod() 
{ 
    return &dog; 
} 

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

+0

Я помню, что могут возникнуть проблемы, если у вас есть глобальные инициализаторы в DLL в Windows, но я не могу вспомнить специфику, и это действительно актуально, если код в DLL в любом случае: D – olliej

+0

Мой код * * * находится в DLL :) Какие проблемы я должен знать? – Gili

+0

штопает, если знаю. Лучше всего предположить, что конструктор будет запускаться один раз для каждого раза, когда DLL загружается, поэтому, если dll будет выгружен и перезагружен, он будет воссоздан, поэтому он вроде как разрывает «контракт» Singleton, но не так много вы можете сделать об этом –

2

AFAIK, единственный раз это было сделано безопасно и без мьютексов или предварительной инициализации глобальных экземпляров находится в Мф Wilson's Imperfect C++, в котором обсуждается, как это сделать, используя «мьютез спина». Я не рядом с моей копией, поэтому не могу сказать вам точно, в это время.

IIRC, есть некоторые примеры использования этого в библиотеках STLSoft, хотя я не могу вспомнить, какие компоненты в это время.

4

Не уверен, означает ли это то, что вы имеете в виду или нет, но вы можете удалить зависимость повышения в системах POSIX, вместо этого вместо этого вызывается pthread_once. Я думаю, вам придется делать что-то другое в Windows, но, избегая этого, именно поэтому boost имеет библиотеку потоков в первую очередь и почему люди платят цену в зависимости от нее.

Выполнение чего-либо «поточно-безопасного» по сути связано с реализацией ваших потоков. Вы должны зависеть от что-то, даже если это только зависящая от платформы модель памяти. В чистом C++ 03 просто невозможно предположить что-либо вообще о потоках, которые выходят за рамки языка.

Смежные вопросы