zoukankan      html  css  js  c++  java
  • 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. 水平有限,望及时指出错误。谢谢

      

  • 相关阅读:
    Apollo,Python,Delphi与Oracle之间的神话关系
    Delphi语言获得生命的原因和过程
    cocos2d(x) HTML label ;CCHTML CCHTMLLabel
    不重启使XP环境变量生效
    对当下纷繁乱世的思考框架(核心与边缘,时间与成本)
    晚明一出,明朝不必再穿越了
    常用的Linux终端
    如何发布使用LGPL版Qt的商业软件
    如何制作一个类似Tiny Wings的游戏 Cocos2d-x 2.1.4
    文明之火熊熊燃烧,灼热乃至引燃了周边霉湿的柴草
  • 原文地址:https://www.cnblogs.com/newbeeyu/p/6885098.html
Copyright © 2011-2022 走看看