Ориентирована на многопотоковое исполнение реализация Meyers' Шаблона "одиночка"?

Эти _t по сути не имеет никакого особого значения. Но это попало в общее использование для добавления эти _t суффикс к определению типа.

можно быть более знакомы с общими методами C для переменного именования... Это подобно тому, как распространено засунуть p в передней стороне для указателя и использовать подчеркивание перед глобальными переменными (это немного менее распространено), и использовать имена переменной i, j, и k для временных переменных цикла.

В коде, где размер слова и упорядочивание важны, очень распространено использовать пользовательские определенные типы, которые являются явными, такой как BYTE WORD (обычно 16-разрядный) DWORD (32 бита).

int_t не так хорошо, потому что определение int варьируется между платформами - поэтому, чей int Вы соответствуете? (Хотя в эти дни большая часть центральной ПК разработки рассматривает его, как 32 бита, много материала для некомпьютерной разработки все еще рассматривает интервал как 16 битов).

129
задан Rakete1111 20 June 2017 в 13:05
поделиться

6 ответов

В C ++ 11 он является потокобезопасным. Согласно стандарту , §6.7 [stmt.dcl] p4 :

Если элемент управления входит объявление одновременно с инициализацией переменной, одновременное выполнение должно ждать завершения инициализации.

GCC и VS поддерживают эту функцию ( Динамическая инициализация и уничтожение с параллелизмом , также известная как Magic Statics в MSDN ) выглядит следующим образом:

Благодаря @Mankarse и @olen_gam за их комментарии


В C ++ 03 этот код не был потокобезопасным. Есть статья Мейерса под названием «C ++ и опасности двойной проверки блокировки» , в которой обсуждаются потокобезопасные реализации этого шаблона, и вывод более или менее такой:

154
ответ дан 24 November 2019 в 00:33
поделиться

В следующем стандарте (раздел 6.7.4) объясняется, как статическая локальная инициализация является потокобезопасной. Так что, как только этот раздел стандарта будет широко реализован, предпочтительной реализацией станет синглтон Мейера

. Я уже не согласен со многими ответами. Большинство компиляторов уже реализуют статическую инициализацию таким образом. Единственным заметным исключением является Microsoft Visual Studio.

9
ответ дан 24 November 2019 в 00:33
поделиться

Правильный ответ зависит от вашего компилятора. Он может решить сделать потокобезопасным; это не «естественно» потокобезопасно.

6
ответ дан 24 November 2019 в 00:33
поделиться

Является ли следующая реализация [...] потокобезопасной?

На большинстве платформ она не является потокобезопасной. (Добавьте обычный отказ от ответственности, поясняющий, что стандарт C ++ не знает о потоках, поэтому с юридической точки зрения он не говорит, есть это или нет.)

Если нет, то почему [...]?

Причина этого в том, что ничто не мешает более чем одному потоку одновременно выполнять конструктор s '.

как сделать его потокобезопасным?

«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.

как сделать его потокобезопасным?

«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.

как сделать его потокобезопасным?

«C ++ и опасности двойной проверки блокировки» Скотта Мейерса и Андрея Александреску - довольно хороший трактат на тему поточно-безопасных синглтонов.

5
ответ дан 24 November 2019 в 00:33
поделиться

Как сказал MSalters: Это зависит от используемой вами реализации C ++. Проверить документацию. Что касается другого вопроса: «Если нет, то почему?» - Стандарт C ++ еще ничего не упоминает о потоках. Но предстоящая версия C ++ знает о потоках и явно заявляет, что инициализация статических локальных переменных является потокобезопасной. Если два потока вызывают такую ​​функцию, один поток выполнит инициализацию, а другой заблокируется и будет ждать ее завершения.

2
ответ дан 24 November 2019 в 00:33
поделиться

Чтобы ответить на ваш вопрос о том, почему это не потокобезопасно, это не потому, что первый вызов 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 для обеспечения независимой от платформы оболочки для блокировки.
  • это гарантирует безопасность потока, когда экземпляр Singleton запрашивается после вызова main () - если вы вызываете его до этого (как при инициализации статического объекта), все может не работать, потому что CRITICAL_SECTION может не быть инициализирован.
  • блокировка должна производиться каждый раз, когда запрашивается экземпляр. Как я уже сказал, это простая потокобезопасная реализация. Если вам нужен лучший вариант (или вы хотите узнать, почему такие вещи, как метод двойной проверки блокировки, ошибочны), см. Статьи , на которые есть ссылка в ответе Groo .
19
ответ дан 24 November 2019 в 00:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: