Возможно (... да, верно) решено: дважды проверил блокировку на C++: новичок в указателе температуры, затем назначить его экземпляру

Это продолжение этого поста о двойной проверке блокировки. Я пишу новое сообщение, потому что кажется, что публикация продолжения "старых" сообщений не делает его таким же видимым / активным, как отправка нового сообщения, возможно, потому, что большинство людей не сортируют сообщения в stackru по уровню активности,

Всем, кто ответил, спасибо за ваш вклад в оригинальный пост. Посмотрев замечательную книгу Джо Даффи "Параллельное программирование в Windows", я подумал, что мне следует использовать приведенный ниже код. Он во многом идентичен коду из его книги, за исключением некоторых переменных переименований и строки InterlockedXXX. Следующая реализация использует:

  1. ключевое словоvolatile как на временные, так и на "фактические" указатели для защиты от повторного упорядочивания компилятором.
  2. InterlockedCompareExchangePointer для защиты от переупорядочения с процессором.

Итак, это должно быть довольно безопасно (... верно?):

template <typename T>
class LazyInit {
public:
    typedef T* (*Factory)();
    LazyInit(Factory f = 0) 
        : factory_(f)
        , singleton_(0)
    {
        ::InitializeCriticalSection(&cs_);
    }

    T& get() {
        if (!singleton_) {
            ::EnterCriticalSection(&cs_);
            if (!singleton_) {
                T* volatile p = factory_();
                // Joe uses _WriterBarrier(); then singleton_ = p;
                // But I thought better to make singleton_ = p atomic (as I understand, 
                // on Windows, pointer assignments are atomic ONLY if they are aligned)
                // In addition, the MSDN docs say that InterlockedCompareExchangePointer
                // sets up a full memory barrier.
                ::InterlockedCompareExchangePointer((PVOID volatile*)&singleton_, p, 0);
            }
            ::LeaveCriticalSection(&cs_);
        }
        #if PREVENT_IA64_FROM_REORDERING
        _ReadBarrier();
        #endif
        return *singleton_;
    }

    virtual ~LazyInit() {
        ::DeleteCriticalSection(&cs_);
    }
private:
    CRITICAL_SECTION cs_;
    Factory factory_;
    T* volatile singleton_;
};

1 ответ

Я всегда использовал гораздо более простой шаблон синглтона.

class CSomething
{
protected:
    CSomething() {};
public:
    ~CSomething() {};
    static CSomething *Get()
    {
        static CSomething s;
        return &s;
    }
    // Rest of the class
};
Другие вопросы по тегам