首页 > 代码库 > c++之单例模式

c++之单例模式

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++之单例模式