美文网首页
boost中的单例模式(singleton)

boost中的单例模式(singleton)

作者: 云石畅想 | 来源:发表于2019-03-28 22:08 被阅读0次

    coolshell上有篇文章将单例,讲的已经很好了,最近看了boost的实现,感觉更有一些体会。
    原文使用的是java来进行讲解,涉及到了并发的场景。但我这里要解释的,是c++中的单例模式,当然,是通过学习boost的实现。 (boost 1.57.0)

    boost中有一些分散的单例实现,能够独立摘出来用的主要有以下四个:

    • boost/container/detail/singleton.hpp
    • boost/serialization/singleton.hpp
    • boost/thread/detail/singleton.hpp
    • boost/pool/singleton_pool.hpp
      尤其以前两个为主。

    1. boost/container/detail/singleton.hpp

    代码如下:

    // T must be: no-throw default constructible and no-throw destructible
    template <typename T>
    struct singleton_default
    {
      private:
        struct object_creator
        {
          // This constructor does nothing more than ensure that instance()
          //  is called before main() begins, thus creating the static
          //  T object before multithreading race issues can come up.
          object_creator() { singleton_default<T>::instance(); }
          inline void do_nothing() const { }
        };
        static object_creator create_object;
     
        singleton_default();
     
      public:
        typedef T object_type;
     
        // If, at any point (in user code), singleton_default<T>::instance()
        //  is called, then the following function is instantiated.
        static object_type & instance()
        {
          // This is the object that we return a reference to.
          // It is guaranteed to be created before main() begins because of
          //  the next line.
          static object_type obj;
     
          // The following line does nothing else than force the instantiation
          //  of singleton_default<T>::create_object, whose constructor is
          //  called before main() begins.
          create_object.do_nothing();
     
          return obj;
        }
    };
    template <typename T>
    typename singleton_default<T>::object_creator
    singleton_default<T>::create_object;
    

    其实注释已经说得很清楚了,这个实现使用了一个struct object_creator来作为类的static成员变量,单例的实体是obj,并不暴露出来,只是作为类的成员函数的static变量(local static对象)。通过create_object在main之前被执行构造来保证单例是在main之前被构造好。
    这个单例是利用main调用之前,程序只有一个线程,来保证单例在多线程下的唯一性。

    其中最迷惑人的也就就是create_object.do_nothing(); 这么用的具体原因,可以参考C++标准中的3.6.2 Initialization of non-local variables:

    1. It is implementation-defined whether the dynamic initialization of a non-local variable with static storage
      duration is done before the first statement of main...

    2. boost/serialization/singleton.hpp

    代码如下:

    class singleton_module : 
        public boost::noncopyable
    {
    private:
        static bool & get_lock(){
            static bool lock = false;
            return lock;
        }
    public:
    //    static const void * get_module_handle(){
    //        return static_cast<const void *>(get_module_handle);
    //    }
        static void lock(){
            get_lock() = true;
        }
        static void unlock(){
            get_lock() = false;
        }
        static bool is_locked() {
            return get_lock();
        }
    };
     
    namespace detail {
     
    template<class T>
    class singleton_wrapper : public T
    {
    public:
        static bool m_is_destroyed;
        ~singleton_wrapper(){
            m_is_destroyed = true;
        }
    };
     
    template<class T>
    bool detail::singleton_wrapper< T >::m_is_destroyed = false;
     
    } // detail
     
    template <class T>
    class singleton : public singleton_module
    {
    private:
        BOOST_DLLEXPORT static T & instance;
        // include this to provoke instantiation at pre-execution time
        static void use(T const &) {}
        BOOST_DLLEXPORT static T & get_instance() {
            static detail::singleton_wrapper< T > t;
            // refer to instance, causing it to be instantiated (and
            // initialized at startup on working compilers)
            BOOST_ASSERT(! detail::singleton_wrapper< T >::m_is_destroyed);
            use(instance);
            return static_cast<T &>(t);
        }
    public:
        BOOST_DLLEXPORT static T & get_mutable_instance(){
            BOOST_ASSERT(! is_locked());
            return get_instance();
        }
        BOOST_DLLEXPORT static const T & get_const_instance(){
            return get_instance();
        }
        BOOST_DLLEXPORT static bool is_destroyed(){
            return detail::singleton_wrapper< T >::m_is_destroyed;
        }
    };
     
    template<class T>
    BOOST_DLLEXPORT T & singleton< T >::instance = singleton< T >::get_instance();
    

    这个实现和detail中的类似,同样通过static变量来完成单例的第一次调用,来保证在main之前构造好,同样使用了一个use来显示的调用static成员变量,触发3.6.2 Initialization of non-local variables。唯一不同的是单例的接口支持了mutable,但是看不出来实际中有什么作用,注释中说了,在debug中可以通过设置lock来测试修改全体单例的地方,可能这个就是用处吧。

    3. boost/thread/detail/singleton.hpp

    代码:

    // class singleton has the same goal as all singletons: create one instance of
    // a class on demand, then dish it out as requested.
     
    template <class T>
    class singleton : private T
    {
    private:
        singleton();
        ~singleton();
     
    public:
        static T &instance();
    };
     
     
    template <class T>
    inline singleton<T>::singleton()
    {
        /* no-op */
    }
     
    template <class T>
    inline singleton<T>::~singleton()
    {
        /* no-op */
    }
     
    template <class T>
    /*static*/ T &singleton<T>::instance()
    {
        // function-local static to force this to work correctly at static
        // initialization time.
        static singleton<T> s_oT;
        return(s_oT);
    }
    

    这个thread中的实现就清晰的多,只不过单例是在调用时第一次访问,而不是在main之前,这对于多线程程序来说,要格外注意的。
    不过在c++11后,要求编译器保证内部静态变量的线程安全性,因此在支持c++11的编译器中这种方法可以产生线程安全的单例模式,然而在不支持c++11的编译器中,这种方法无法得到保证。

    4. boost/pool/singleton_pool.hpp

    代码:

    template <typename Tag,
        unsigned RequestedSize,
        typename UserAllocator,
        typename Mutex,
        unsigned NextSize,
        unsigned MaxSize >
    class singleton_pool
    {
      public:
        typedef Tag tag; /*!< The Tag template parameter uniquely
                         identifies this pool and allows
          different unbounded sets of singleton pools to exist.
          For example, the pool allocators use two tag classes to ensure that the
          two different allocator types never share the same underlying singleton pool.
          Tag is never actually used by singleton_pool.
        */
        typedef Mutex mutex; //!< The type of mutex used to synchonise access to this pool (default <tt>details::pool::default_mutex</tt>).
        typedef UserAllocator user_allocator; //!< The user-allocator used by this pool, default = <tt>default_user_allocator_new_delete</tt>.
        typedef typename pool<UserAllocator>::size_type size_type; //!< size_type of user allocator.
        typedef typename pool<UserAllocator>::difference_type difference_type; //!< difference_type of user allocator.
     
        BOOST_STATIC_CONSTANT(unsigned, requested_size = RequestedSize); //!< The size of each chunk allocated by this pool.
        BOOST_STATIC_CONSTANT(unsigned, next_size = NextSize); //!< The number of chunks to allocate on the first allocation.
     
    private:
        singleton_pool();
     
    #ifndef BOOST_DOXYGEN
        struct pool_type: public Mutex, public pool<UserAllocator>
        {
          pool_type() : pool<UserAllocator>(RequestedSize, NextSize, MaxSize) {}
        }; //  struct pool_type: Mutex
     
    #else
        //
        // This is invoked when we build with Doxygen only:
        //
    public:
        static pool<UserAllocator> p; //!< For exposition only!
    #endif
     
     
      public:
        static void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
        { //! Equivalent to SingletonPool::p.malloc(); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return (p.malloc)();
        }
        static void * ordered_malloc()
        {  //! Equivalent to SingletonPool::p.ordered_malloc(); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return p.ordered_malloc();
        }
        static void * ordered_malloc(const size_type n)
        { //! Equivalent to SingletonPool::p.ordered_malloc(n); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return p.ordered_malloc(n);
        }
        static bool is_from(void * const ptr)
        { //! Equivalent to SingletonPool::p.is_from(chunk); synchronized.
          //! \returns true if chunk is from SingletonPool::is_from(chunk)
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return p.is_from(ptr);
        }
        static void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const ptr)
        { //! Equivalent to SingletonPool::p.free(chunk); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          (p.free)(ptr);
        }
        static void ordered_free(void * const ptr)
        { //! Equivalent to SingletonPool::p.ordered_free(chunk); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          p.ordered_free(ptr);
        }
        static void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const ptr, const size_type n)
        { //! Equivalent to SingletonPool::p.free(chunk, n); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          (p.free)(ptr, n);
        }
        static void ordered_free(void * const ptr, const size_type n)
        { //! Equivalent to SingletonPool::p.ordered_free(chunk, n); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          p.ordered_free(ptr, n);
        }
        static bool release_memory()
        { //! Equivalent to SingletonPool::p.release_memory(); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return p.release_memory();
        }
        static bool purge_memory()
        { //! Equivalent to SingletonPool::p.purge_memory(); synchronized.
          pool_type & p = get_pool();
          details::pool::guard<Mutex> g(p);
          return p.purge_memory();
        }
     
    private:
       typedef boost::aligned_storage<sizeof(pool_type), boost::alignment_of<pool_type>::value> storage_type;
       static storage_type storage;
     
       static pool_type& get_pool()
       {
          static bool f = false;
          if(!f)
          {
             // This code *must* be called before main() starts, 
             // and when only one thread is executing.
             f = true;
             new (&storage) pool_type;
          }
     
          // The following line does nothing else than force the instantiation
          //  of singleton<T>::create_object, whose constructor is
          //  called before main() begins.
          create_object.do_nothing();
     
          return *static_cast<pool_type*>(static_cast<void*>(&storage));
       }
     
       struct object_creator
       {
          object_creator()
          {  // This constructor does nothing more than ensure that instance()
             //  is called before main() begins, thus creating the static
             //  T object before multithreading race issues can come up.
             singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::get_pool();
          }
          inline void do_nothing() const
          {
          }
       };
       static object_creator create_object;
    }; // struct singleton_pool
     
    template <typename Tag,
        unsigned RequestedSize,
        typename UserAllocator,
        typename Mutex,
        unsigned NextSize,
        unsigned MaxSize >
    typename singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::storage_type singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::storage;
     
    template <typename Tag,
        unsigned RequestedSize,
        typename UserAllocator,
        typename Mutex,
        unsigned NextSize,
        unsigned MaxSize >
    typename singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::object_creator singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::create_object;
    

    这个是在pool中的单例实现,所有的接口都和pool类似,使用也类似,单例的实现类似于detail的实现,包括do_nothing()的使用,显示线程安全和在main之前构造好的,细节参考detail的实现。

    看了上面4个单例的实现,基本上1最典型,保证了main之前的调用。但是boost为什么没有一个通用的单例类呢?是因为Are Singletons really that bad?

    可以在stackoverflow搜搜这个讨论,可能这也是boost没有一个通用的单例类的原因,已提供的单例类,都是在某个具体的模块中。学习不代表要使用。

    相关文章

      网友评论

          本文标题:boost中的单例模式(singleton)

          本文链接:https://www.haomeiwen.com/subject/mvdhbqtx.html