RPCZ中的智能指针单例

(金庆的专栏)

智能指针单例应用于 RPCZ 库以实现库的自动初始化与自动清理.
RPCZ: RPC implementation for Protocol Buffers over ZeroMQ
https://github.com/jinq0123/rpcz

代码来自: (A dynamic) Singleton using weak_ptr and shared_ptr
http://boost.2283326.n4.nabble.com/A-dynamic-Singleton-using-weak-ptr-and-shared-ptr-td2581447.html

以下为该文摘译与代码整理.
原作者 Martin Ba,
回复者 David Rodríguez Ibeas 提出了优化意见, 并被原作者认同.
本作者提出进一步优化, 附于本文尾部.

Singleton using boost weak_ptr and shared_ptr
应用 boost weak_ptr 和 shared_ptr 实现单例
------------------------------------------------------------------
by Martin Ba

Requirement: Singleton that is constructed on first use (not on process
start) and destroyed after the last "client-code" has finished with it.

需求: 初次使用时构造单例(而非进程开始时构造),
并且在最后的客户代码使用完后就销毁单例.

Note: It is therefore possible that more that one Singleton instances
exist within a process's lifetime, BUT there must only be at most one
Object active at any given time (Construction must not run before
destruction has finished.

注意: 因此有可能进程的生命期内会存在多个单例实例, 但是,
任一时刻最多只会有一个对象(析造完成后才允许新的构造).

Starting point: http://lists.boost.org/boost-users/2002/10/2014.php

Problem of the simple solution: No protection against multiple
initialization and against simultaneous deletion and construction.

简单方案的问题: 没有对同时初始化, 或者同时删除和构造进行保护.

Solution: The construction and destruction of the singleton instance(s)
has to be protected additionally.

解决方案: 对单例的构造和析构额外添加保护.

(金庆: 原代码中的 class atomic_bool 已替换为 boost::atomic_bool, 以简化代码.)

dynamic_singleton.h

#pragma once

#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>

class dynamic_singleton : private boost::noncopyable
{
public:
    typedef boost::shared_ptr<dynamic_singleton> shared_t;
    static shared_t get_instance();

    // Interface:
    void example(int cookie);
    // ...

private:
    dynamic_singleton();
    virtual ~dynamic_singleton();

    struct impl;
    typedef boost::scoped_ptr<impl> impl_t;
    impl_t pimpl;

    struct deleter;
    friend struct deleter;
};

dynamic_singleton.cpp

#include "dynamic_singleton.h"

#include <boost/atomic.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread.hpp>

#define MESG(msg)   \
    printf("%s\n", msg); \
    /**/

struct dynamic_singleton::impl : private boost::noncopyable
{
    impl()  {}
    ~impl() {}

    static void start_construction()
    {
        boost::xtime spin_time;
        spin_time.sec = 1;

        while(the_object_exists) {
            boost::thread::sleep(spin_time);
        }
    }

    static void finish_construction()
    {
        assert(!the_object_exists);
        the_object_exists = true;
    }

    static void finish_destruction()
    {
        assert(the_object_exists);
        the_object_exists = false;
    }

    typedef boost::weak_ptr<dynamic_singleton> internal_shared_t;
    static internal_shared_t the_object;

    static boost::recursive_mutex sync_init;
    static boost::atomic_bool the_object_exists;
};

dynamic_singleton::impl::internal_shared_t
    dynamic_singleton::impl::the_object;
boost::recursive_mutex
    dynamic_singleton::impl::sync_init;
boost::atomic_bool
    dynamic_singleton::impl::the_object_exists;

struct dynamic_singleton::deleter
{
    void operator() (dynamic_singleton* p)
    {
        assert(p);
        delete p;
        impl::finish_destruction();
    }
};

dynamic_singleton::shared_t dynamic_singleton::get_instance()
{
    // Syncronise Initialization:
    boost::recursive_mutex::scoped_lock lock(impl::sync_init);
    MESG(__FUNCTION__);

    // Acquire singleton pointer:
    shared_t object_ptr = impl::the_object.lock();

    if(!object_ptr.use_count()) {
        impl::start_construction();
        object_ptr.reset(new dynamic_singleton(), dynamic_singleton::deleter());
        impl::the_object = object_ptr;
        impl::finish_construction();
    }
    return object_ptr;
}

dynamic_singleton::dynamic_singleton()
{
    pimpl.reset(new impl());
    MESG(__FUNCTION__);

    // For example open a unique system or process global resource
    printf(" >> Singleton opens the global resouce.\n");
}

dynamic_singleton::~dynamic_singleton()
{
    MESG(__FUNCTION__);

    // For example close a unique system or process global resource
    printf(" << Singleton closes the global resouce.\n");
}

void dynamic_singleton::example(int cookie)
{
    printf("%s(%d)\n", __FUNCTION__, cookie);
}

main.cpp

#include "dynamic_singleton.h"

#include <iostream>
#include <boost/thread.hpp>

struct singleton_user
{
    explicit singleton_user(int num)
        : num_(num)
    { }

    void operator()()
    {
        using namespace std;
        printf("%d uses singleton ...\n", num_);
        dynamic_singleton::shared_t s = dynamic_singleton::get_instance();
        s->example(num_);
    }

    int num_;
};

int main(int argc, char* argv[])
{
    boost::thread t1( singleton_user(1) );
    boost::thread t2( singleton_user(2) );
    boost::thread t3( singleton_user(3) );
    boost::thread t4( singleton_user(4) );
    boost::thread t5( singleton_user(5) );

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();

    return 0;
}

David Rodríguez Ibeas 提出建议:

* 用 condition 代替 sleep().
* 因为 condition 已有 mutex, 所以 the_object_exists 从 atomic_bool 改为 bool.
* 不需要创建 dynamic_singleton::impl 实例, 所以其构造析构改为私有.
* 把 the_object_exists = true; 从 finish_destruction() 移到 start_construction(),
  finish_destruction()成为空函数可删除.
 
// Using boost::condition:
struct dynamic_singleton::impl : private boost::noncopyable
{  
    static void start_construction()
    {  
        boost::recursive_mutex::scoped_lock lock( sync_ );
        while ( the_object_exists ) {
            cond_.wait( lock );
        }
        the_object_exists = true;
    }
    static void finish_destruction()
    {  
        boost::recursive_mutex::scoped_lock lock( sync_ );
        the_object_exists = false;
        cond_.notify_one();
    }
    typedef boost::weak_ptr<dynamic_singleton> internal_shared_t;
    static internal_shared_t the_object;

    static boost::recursive_mutex sync_init;
    static boost::recursive_mutex sync_;    // moved from atomic_bool
    static bool the_object_exists;  // plain bool, synch'ed with sync_
    static boost::condition_variable_any cond_;

private:
    impl() {}
    ~impl() {}
};

dynamic_singleton::impl::internal_shared_t dynamic_singleton::impl::the_object;
boost::recursive_mutex dynamic_singleton::impl::sync_init;
boost::recursive_mutex dynamic_singleton::impl::sync_;
bool dynamic_singleton::impl::the_object_exists = false;
boost::condition_variable_any dynamic_singleton::impl::cond_;

RPCZ 中的进一步优化:

* get_instance()多数情况下不需要加锁, 仅当需要初始化时才加锁

dynamic_singleton::shared_t dynamic_singleton::impl::get_instance()
{
    shared_t object_ptr = impl::the_object.lock();
    if (object_ptr) return object_ptr;

    // Syncronise Initialization:
    boost::recursive_mutex::scoped_lock lock(impl::sync_init);
    ...
}

* get_instance() 内联
* 分离单件相关代码到独立的头文件与实现文件
(impl改名为helper)

dynamic_singleton.h

#pragma once

#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>

class dynamic_singleton : private boost::noncopyable
{
public:
    typedef boost::shared_ptr<dynamic_singleton> shared_t;
    inline static shared_t get_instance();

    // Interface:
    void example(int cookie);
    // ...

private:
    dynamic_singleton();
    virtual ~dynamic_singleton();

    struct helper;
    struct deleter;
    friend struct deleter;
};

#include "dynamic_singleton_helper.h"

inline dynamic_singleton::shared_t dynamic_singleton::get_instance()
{
    return helper::get_instance();
}

dynamic_singleton.cpp

#include "dynamic_singleton.h"

#define MESG(msg)   \
    printf("%s\n", msg); \
    /**/

dynamic_singleton::dynamic_singleton()
{
    MESG(__FUNCTION__);

    // For example open a unique system or process global resource
    printf(" >> Singleton opens the global resouce.\n");
}

dynamic_singleton::~dynamic_singleton()
{
    MESG(__FUNCTION__);

    // For example close a unique system or process global resource
    printf(" << Singleton closes the global resouce.\n");
}

void dynamic_singleton::example(int cookie)
{
    printf("%s(%d)\n", __FUNCTION__, cookie);
}

dynamic_singleton_helper.h

#pragma once

#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>
#include <boost/weak_ptr.hpp>

struct dynamic_singleton::helper : private boost::noncopyable
{
    static inline dynamic_singleton::shared_t get_instance();

    static void start_construction();
    static void finish_destruction();

    typedef boost::weak_ptr<dynamic_singleton> internal_shared_t;
    static internal_shared_t the_object;

    static boost::recursive_mutex sync_init;
    static boost::recursive_mutex sync_;    // moved from atomic_bool
    static bool the_object_exists;  // plain bool, synch'ed with sync_
    static boost::condition_variable_any cond_;

private:
    helper() {}
    ~helper() {}

private:
    static dynamic_singleton::shared_t make_instance();
};

inline dynamic_singleton::shared_t dynamic_singleton::helper::get_instance()
{
    shared_t object_ptr = the_object.lock();
    if (object_ptr) return object_ptr;

    return make_instance();
}

dynamic_singleton_helper.cpp

#include "dynamic_singleton.h"

void dynamic_singleton::helper::start_construction()
{
    boost::recursive_mutex::scoped_lock lock( sync_ );
    while ( the_object_exists ) {
        cond_.wait( lock );
    }
    the_object_exists = true;
}

void dynamic_singleton::helper::finish_destruction()
{
    boost::recursive_mutex::scoped_lock lock( sync_ );
    the_object_exists = false;
    cond_.notify_one();
}

dynamic_singleton::helper::internal_shared_t dynamic_singleton::helper::the_object;
boost::recursive_mutex dynamic_singleton::helper::sync_init;
boost::recursive_mutex dynamic_singleton::helper::sync_;
bool dynamic_singleton::helper::the_object_exists = false;
boost::condition_variable_any dynamic_singleton::helper::cond_;

struct dynamic_singleton::deleter
{
    void operator() (dynamic_singleton* p)
    {
        assert(p);
        delete p;
        helper::finish_destruction();
    }
};

dynamic_singleton::shared_t dynamic_singleton::helper::make_instance()
{
    // Syncronise Initialization:
    boost::recursive_mutex::scoped_lock lock(sync_init);

    // Acquire singleton pointer:
    shared_t object_ptr = the_object.lock();
    if (object_ptr) return object_ptr;

    start_construction();
    object_ptr.reset(new dynamic_singleton(), dynamic_singleton::deleter());
    the_object = object_ptr;
    return object_ptr;
}

代码打包下载:

http://download.csdn.net/detail/jq0123/8467399

RPCZ中的智能指针单例的更多相关文章

  1. OSG中的智能指针

    在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 ...

  2. C++中的智能指针

    一.动态内存管理 通常我们创建动态内存的时候,需要自己管理好内存,也就是说,new出来的对象一定要注意释放掉.下面通过例子可以看到这个问题所在: struct BBE{ int X; int Y; v ...

  3. Boost中的智能指针(转)

    这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...

  4. C++中的智能指针、轻量级指针、强弱指针学习笔记

    一.智能指针学习总结 1.一个非const引用无法指向一个临时变量,但是const引用是可以的! 2.C++中的delete和C中的free()类似,delete NULL不会报"doubl ...

  5. ATL和vc++中的智能指针(分别是CComPtr和_com_ptr_t)

    一.智能指针的概念 智能指针是一个类,不是指针,智能指针在所包含的指针不再被使用时候会自动释放该所包含指针所占用的系统资源,而不用手动释放. 原理:智能指针封装了包含指针的AddRef()函数和Rel ...

  6. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

  7. 智能指针类模板(中)——Qt中的智能指针

    Qt中的智能指针-QPointer .当其指向的对象被销毁时,它会被自动置空 .析构时不会自动销毁所指向的对象-QSharedPointer .引用计数型智能指针 .可以被自由的拷贝和赋值 .当引用计 ...

  8. 智能指针类模板(上)——STL中的智能指针

    智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...

  9. C++中的智能指针类模板

    1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...

随机推荐

  1. ●BZOJ 1855 [Scoi2010]股票交易

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1855 题解: DP,单调队列优化.(好久没做 DP题,居然还意外地想出来了) 定义 dp[i ...

  2. HDU2256(矩阵)

    求(sqrt(2) + sqrt(3)) ^ 2n MOD 1024 强行盗图- -,这公式推得 .. #include <iostream> #include <cstdio> ...

  3. 获得只有 [年 月 日] 的Date 对象

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); String sDate = sim ...

  4. SpringMVC注解@Component、@Repository、@Service、@Controller区别

    SpringMVC中四个基本注解: @Component.@Repository   @Service.@Controller 看字面含义,很容易却别出其中三个: @Controller   控制层, ...

  5. c++ public,protected,private

    基类的私有成员被继承后不可见(优先级最高) 公有继承不改变基类成员属性 保护继承(私有继承)把基类成员变为保护成员(私有成员) public 公开的 protected 受保护的 private 私有 ...

  6. Linux系统基础优化

    一.关闭防火墙iptables:                (1)关闭                 /etc/init.d/iptables stop                (2)检查 ...

  7. js数组排序,支持正反排序以及多维度排序

    工作中遇到js数组排序问题,数组中存储的都是对象,于是就百度了下,利用别人的代码进行修改,最终完成可以倒序.反序,可以进行多维度排序的功能源码如下: /** * js数组排序 支持数字和字符串 * @ ...

  8. 浅谈static其一之不死变量

    在学习汇编的过程中,小有所悟,遂把自己所思所想记下,以便日后查阅. 首先说说我对这个关键字的理解.static字面上就是静止的.静态的.不变的之类的意思,所以在被它修饰之后,应该也会带有这样的一些特点 ...

  9. 用tensorlayer导入Slim模型迁移学习

    上一篇博客[用tensorflow迁移学习猫狗分类]笔者讲到用tensorlayer的[VGG16模型]迁移学习图像分类,那麽问题来了,tensorlayer没提供的模型怎么办呢?别担心,tensor ...

  10. 链表的无锁操作 (JAVA)

    看了下网上关于链表的无锁操作,写的不清楚,遂自己整理一部分,主要使用concurrent并发包的CAS操作. 1. 链表尾部插入 待插入的节点为:cur 尾节点:pred 基本插入方法: do{ pr ...