1. 程式人生 > >c++之單例模式

c++之單例模式

lsi 但是 desc 模式 單例模式 ron spl 希望 構造函數

1 本篇主要討論下多線程下的單例模式實現:

  首先是 double check 實現方式: 這種模式可以滿足多線程環境下,只產生一個實例。

    template<typename T>
    class dclsingleton
    {
        public:
            static T& GetInstance()
            {
                if(NULL == value_)
                {
                    MutexGuard mg(mutex_);
                    
if (NULL == value_) { value_ = new T; } } return *value_; } protected: dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; } dclsingleton(
const dclsingleton &dcl) {} private: static T* value_; static Mutex mutex_; };

  但是這種實現存在除bug的隱患, 問題就在: value_ = new T; 上。《程序員的自我修養》上指出:

  這樣的代碼是有問題的,問題的來源在於 cpu 的亂序執行。c++裏的new 包含了兩步。

  (1)分配內存

  (2)調用構造函數

  所以 value_ = new T; 實際上包含了三步:

  (1)分配內存

  (2)在分配內存的位置上調用構造函數  

  (3)將內存地址賦值給 value_;

  這三步中,(2), (3)兩步是可以顛倒的,也就是說,可能出現,先執行(3)這是 value_已經不為NULL, 當出現另一個對GetInstance的並發調用,if 內的 value_ != NULL於是返回,但是還沒有調用構造函數。於是使用這個指針的時候,就會導致崩潰。

  這時候需要保證(2), (3)的執行順序,通常需要加上內存屏障,保證一定保證(2)執行完以後,再執行(3)

  這裏我加上了__sync_synchronize(); 後 實現是這樣的:

  

       static T& GetInstance()
            {
                if(NULL == value_)
                {
                    MutexGuard mg(mutex_);
                    if (NULL == value_)
                    {
                        T* tmp = static_cast<T*>(operator new (sizeof(T)));
                        new (tmp) T();
                        __sync_synchronize();
                        value_ = tmp;
                    }
                }

                return *value_;
             }

    這樣便可以既保證多線程環境安全,又保證不會出現上面的問題。

2. 加上內存屏障的示例代碼:dcl_single.h

  

技術分享
#ifndef __DCL_SINGLE_H
#define __DCL_SINGLE_H

#include <iostream>

namespace yl
{
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&mutex_, NULL);
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&mutex_);
        }

    public:
        void Lock()
        {
            pthread_mutex_lock(&mutex_);
        }
        void UnLock()
        {
            pthread_mutex_unlock(&mutex_);
        }

    private:
        pthread_mutex_t mutex_;
    };

    class MutexGuard
    {
    public:
        MutexGuard(Mutex& m) : mutex_(m)
        {
            mutex_.Lock();
        }

        ~MutexGuard()
        {
            mutex_.UnLock();
        }

    private:
        Mutex mutex_;
    };

    template<typename T>
    class dclsingleton
    {
        public:
            static T& GetInstance()
            {
                if(NULL == value_)
                {
                    MutexGuard mg(mutex_);
                    if (NULL == value_)
                    {
                        T* tmp = static_cast<T*>(operator new (sizeof(T)));
                        new (tmp) T();
                        __sync_synchronize();
                        value_ = tmp;
                    }
                }

                return *value_;
            }
        protected:
            dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; }
            dclsingleton(const dclsingleton &dcl) {}

        private:
            static T* value_;
            static Mutex mutex_;
    };

    template<typename T>
    T* dclsingleton<T>::value_ = NULL;

    template<typename T>
    Mutex dclsingleton<T>::mutex_;
}


#endif
View Code

  singletonTest.cpp

#include "dcl_single.h"
#include <iostream>

namespace yl
{
    class MgrSg : public dclsingleton<MgrSg>
    {
        
    private:
        friend class dclsingleton <MgrSg>;
        MgrSg(){ std::cout << "MgrSg: constructor called" << std::endl; }
        ~MgrSg() { std::cout << "MgrSg: desconstructor called" << std::endl; }
    public:
        void print()
        {
            std::cout << "print called" << std::endl;
        }
    };
}

int
main(void)
{
    using namespace yl;

    MgrSg::GetInstance().print();
    return 0;
}

3. 還可以用 unix 下的 pthread_once 來實現單例模式:

  

template <typename T>
        class MySingleton
        {
            public:
                static T & getInstance()
                {
                    pthread_once(&ponce_, &MySingleton::init);
                    return *instance;
                }

            protected:
                MySingleton() {}
                MySingleton(const MySingleton&) {}
            private:
                static void init()
                {
                    instance = new T();
                }
            private:
                static pthread_once_t ponce_;
                static T *instance;
        };

    template<typename T>
        pthread_once_t MySingleton<T>::ponce_ = PTHREAD_ONCE_INIT;

    template<typename T>
        T *MySingleton<T>::instance = nullptr;
}

4.我自己遇到的就是以上兩種情況,若是希望了解更全面,可參考下邊:

  http://www.cnblogs.com/liyuan989/p/4264889.html

5. 水平有限,望及時指出錯誤。謝謝

  

c++之單例模式