Эти _t
по сути не имеет никакого особого значения. Но это попало в общее использование для добавления эти _t
суффикс к определению типа.
можно быть более знакомы с общими методами C для переменного именования... Это подобно тому, как распространено засунуть p в передней стороне для указателя и использовать подчеркивание перед глобальными переменными (это немного менее распространено), и использовать имена переменной i
, j
, и k
для временных переменных цикла.
В коде, где размер слова и упорядочивание важны, очень распространено использовать пользовательские определенные типы, которые являются явными, такой как BYTE
WORD
(обычно 16-разрядный) DWORD
(32 бита).
int_t
не так хорошо, потому что определение int
варьируется между платформами - поэтому, чей int
Вы соответствуете? (Хотя в эти дни большая часть центральной ПК разработки рассматривает его, как 32 бита, много материала для некомпьютерной разработки все еще рассматривает интервал как 16 битов).
В C ++ 11 он является потокобезопасным. Согласно стандарту , §6.7 [stmt.dcl] p4
:
Если элемент управления входит объявление одновременно с инициализацией переменной, одновременное выполнение должно ждать завершения инициализации.
GCC и VS поддерживают эту функцию ( Динамическая инициализация и уничтожение с параллелизмом , также известная как Magic Statics в MSDN ) выглядит следующим образом:
Благодаря @Mankarse и @olen_gam за их комментарии
В C ++ 03 этот код не был потокобезопасным. Есть статья Мейерса под названием «C ++ и опасности двойной проверки блокировки» , в которой обсуждаются потокобезопасные реализации этого шаблона, и вывод более или менее такой:
В следующем стандарте (раздел 6.7.4) объясняется, как статическая локальная инициализация является потокобезопасной. Так что, как только этот раздел стандарта будет широко реализован, предпочтительной реализацией станет синглтон Мейера
. Я уже не согласен со многими ответами. Большинство компиляторов уже реализуют статическую инициализацию таким образом. Единственным заметным исключением является Microsoft Visual Studio.
Правильный ответ зависит от вашего компилятора. Он может решить сделать потокобезопасным; это не «естественно» потокобезопасно.
Является ли следующая реализация [...] потокобезопасной?
На большинстве платформ она не является потокобезопасной. (Добавьте обычный отказ от ответственности, поясняющий, что стандарт C ++ не знает о потоках, поэтому с юридической точки зрения он не говорит, есть это или нет.)
Если нет, то почему [...]?
Причина этого в том, что ничто не мешает более чем одному потоку одновременно выполнять конструктор s
'.
как сделать его потокобезопасным?
«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.
как сделать его потокобезопасным?
«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.
как сделать его потокобезопасным?
«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.
Как сказал MSalters: Это зависит от используемой вами реализации C ++. Проверить документацию. Что касается другого вопроса: «Если нет, то почему?» - Стандарт C ++ еще ничего не упоминает о потоках. Но предстоящая версия C ++ знает о потоках и явно заявляет, что инициализация статических локальных переменных является потокобезопасной. Если два потока вызывают такую функцию, один поток выполнит инициализацию, а другой заблокируется и будет ждать ее завершения.
Чтобы ответить на ваш вопрос о том, почему это не потокобезопасно, это не потому, что первый вызов instance ()
должен вызвать конструктор для Singleton s
. Чтобы обеспечить безопасность потоков, это должно происходить в критическом разделе, но в стандарте нет требования, чтобы критический раздел был взят (на сегодняшний день стандарт полностью молчит о потоках). Компиляторы часто реализуют это с помощью простой проверки и увеличения статического логического значения, но не в критическом разделе. Что-то вроде следующего псевдокода:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof( Singleton)];
if (!initialized) {
initialized = true;
new( &s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>( &s)));
}
Итак, вот простой потокобезопасный синглтон (для Windows). Он использует простую оболочку класса для объекта Windows CRITICAL_SECTION, чтобы мы могли заставить компилятор автоматически инициализировать CRITICAL_SECTION
перед вызовом main ()
. В идеале должен использоваться настоящий класс критического раздела RAII, который может иметь дело с исключениями, которые могут возникнуть, когда критический раздел удерживается, но это выходит за рамки этого ответа.
Основная операция заключается в том, что когда экземпляр Singleton
запрашивается, берется блокировка, создается синглтон, если он нужен, затем блокировка снимается и возвращается ссылка синглтона.
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection( this);
}
~CritSection() {
DeleteCriticalSection( this);
}
private:
// disable copy and assignment of CritSection
CritSection( CritSection const&);
CritSection& operator=( CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton( Singleton const&);
Singleton& operator=( Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection( &instance_lock);
static Singleton s;
LeaveCriticalSection( &instance_lock);
return s;
}
Чувак - это много чуши, чтобы «сделать лучше глобальный».
Основные недостатки этой реализации (если я не позволил проскользнуть некоторым ошибкам):
new Singleton ()
выдает ошибку, блокировка не снимается. Это можно исправить, используя настоящий объект блокировки RAII вместо простого, который у меня есть. Это также может помочь сделать вещи переносимыми, если вы используете что-то вроде Boost для обеспечения независимой от платформы оболочки для блокировки. main ()
- если вы вызываете его до этого (как при инициализации статического объекта), все может не работать, потому что CRITICAL_SECTION
может не быть инициализирован.