用C++实现单例模式3——如何在不使用锁和C++11的情况下,用C++实现线程安全的Singleton

如题所示,在这里主要讲的是,如何在不使用锁和C++11的情况下,用C++实现线程安全的Singleton。
有四种方式来实现:
        1.Atomic Singleton
        2.UNIX平台下的pthread_once
        3.static object
        4.local static
需要区分三种状态:
        *对象已经构造完成
        *对象还没有构造完成,但是某一线程正在构造中
        *对象还没有构造完成,也没有任何线程正在构造中

1.Atomic Singleton
    在C++11之前的版本下,除了通过锁实现线程安全的Singleton外,还可以利用各个编译器内置的atomic operation来实现。(假设类Atomic是封装的编译器提供的atomic operation)。
template<typename T>
class Singleton
{
public:
static T& getInstance()
{
    while (true)
    {
        if (ready_.get())
        {
            return *value_;
        }
        else
        {
            if (initializing_.getAndSet(true))
            {
             // another thread is initializing, waiting in circulation
            }
            else
            {
                value_ = new T();
                ready_.set(true);
                return *value_;
            }
        }
    }
}
private:
    Singleton();
    ~Singleton();
    static Atomic<bool> ready_;
    static Atomic<bool> initializing_;
    static T* value_;
};

template<typename T>
Atomic<int> Singleton<T>::ready_(false);

template<typename T>
Atomic<int> Singleton<T>::initializing_(false);

template<typename T>
T* Singleton<T>::value_ = NULL;
2.UNIX平台下的pthread_once
    如果是在unix平台的话,除了使用atomic operation外,在不适用C++11的情况下,还可以通过pthread_once来实现Singleton。
    pthread_once的原型为:
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
        APUE中对于pthread_once是这样说的:
        如果每个线程都调用pthread_once,系统就能保证初始化例程init_routine只被调用一次,即在系统首次调用pthread_once时。
     所以,我们就可以这样来实现Singleton了。
template<typename T>
class Singleton : noncopyable
{
public:
static T& getInstance()
{
    threads::pthread_once(&once_control_, init);
    return *value_;
}
private:
    static void init()
    {
        value_ = new T();
    }
    Singleton();
    ~Singleton();
    static pthread_once_t once_control_;
    static T* value_;
};

template<typename T>
pthread_once_t Singleton<T>::once_control_ = PTHREAD_ONCE_INIT;

template<typename T>
T* Singleton<T>::value_ = NULL;
       如果需要正确的释放资源的话,可以在init函数里面使用glibc提供的atexit函数来注册相关的资源释放函数,从而达到了只在进程退出时才释放资源的这一目的。

3.static object
   不用锁和C++11,那么可以通过atomic operation来实现,但是有人会说atomic不是夸平台的,各个编译器的实现不一样。那么其实通过static object来实现也是可行的。
template<typename T>
class Singleton
{
public:
    static T& getInstance()
    {
        return *value_;
    }
private:
    Singleton();
    ~Singleton();
    class Helper
    {
    public:
        Helper()
        {
            Singleton<T>::value_ = new T();
        }
        ~Helper()
        {
            delete value_;
            value_ = NULL;
        }
    }; //class Helper
    friend class Helper;
    static T* value_;
    static Helper helper_;
};//class Singleton

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

template<typename T>
typename Singleton<T>::Helper Singleton<T>::helper_;
      在进入main之前就把Singleton对象构造出来就可以避免在进入main函数后的多线程环境中构造的各种情况了。这种写法有一个前提就是不能在main函数执行之前调用getInstance,因为C++标准只保证静态变量在main函数之前之前被构造完成。
     可能有人会说如果helper的初始化先于value_初始化的话,那么helper_初始化的时候就会使用尚没有被初始化的value_,这个时候使用其返回的对象就会出现问题,或者在后面value_“真正”初始化的时候会覆盖掉helper_初始化时赋给value_的值。
     实际上这种情况不会发生,value_的初始化一定先于helper_,因为C++标准保证了这一行为:
The storage for objects with static storage duration (basic.stc.static) shall be zero-initialized (dcl.init) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (basic.types) with static storage duration initialized with constant expressions (expr.const) shall be initialized before any dynamic initialization takes place. Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

     stackoverflow中的一个问题也讨论了相关的行为,When are static C++ class members initialized?    http://stackoverflow.com/questions/1421671/when-are-static-c-class-members-initialized


4.local static
    上面一种写法只能在进入main函数后才能调用getInstance,那么有人说,我要在main函数之前调用怎么办?
    嗯,办法还是有的。这个时候我们就可以利用local static来实现,C++标准保证函数内的local static变量在函数调用之前被初始化构造完成,利用这一特性就可以达到目的:
template<typename T>
class Singleton
{
private:
    Singleton();
    ~Singleton();
    class Creater
    {
    public:
        Creater()
        : value_(new T())
        {
        }
        ~Creater()
        {
            delete value_;
            value_ = NULL;
        }
        T& getValue()
        {
            return *value_;
        }
        T* value_;
    };//class  Creater
public:
    static T& getInstance()
    {
        static Creater creater;
        return creater.getValue();
    }
private:
    class Dummy
    {
    public:
        Dummy()
        {
            Singleton<T>::getInstance();
        }
    };//class
    static Dummy dummy_;
};

template<typename T>
typename Singleton<T>::Dummy Singleton<T>::dummy_;
     这样就可以了。dummy_的作用是即使在main函数之前没有调用getInstance,它依然会作为最后一道屏障保证在进入main函数之前构造完成Singleton对象。这样就避免了在进入main函数后的多线程环境中初始化的各种问题了。
     但是此种方法只能在main函数执行之前的环境是单线程的环境下才能正确工作。
     实际上,上文所讲述了各种写法中,有一些不能在main函数之前调用。有一些可以在main函数之前调用,但是必须在进入main之前的环境是单线程的情况下才能正常工作。具体哪种写法是属于这两种情况就不一一分析了。总之,个人建议最好不要在进入main函数之前获取Singleton对象。因为上文中的各种方法都用到了staitc member,而C++标准只保证static member在进入main函数之前初始化,但是不同编译单元之间的static member的初始化顺序却是未定义的, 所以如果在main之前就调用getInstance的话,就有可能出现实现Singleton的static member还没有初始化就被使用的情况。
     如果万一要在main之前获取Singleton对象,并且进入main之前的环境是多线程环境,这种情形下,还能保证正常工作的写法只有C++ 11下的Meyers Singleton,或者如g++ 4.0及其后续版本这样的编译器提前支持内存模型情况下的C++ 03也是可以的。



boost下singleton模式实现:
template <typename T>
struct Singleton
{
struct object_creator
{
    object_creator()
    {
        Singleton<T>::instance();
    }
    inline void do_nothing()const {}
};
static object_creator create_object;
public:
    typedef T object_type;
    static object_type& instance()
    {
        static object_type obj;
        create_object.do_nothing();
        return obj;
    }
};
template <typename T>
typename Singleton<T>::object_creator Singleton<T>::create_object;
// int main()
// {
// int sint = Singleton<int>::instance();
// return 0;
// }
代码分析,可以参考fullsail博客:BOOST的Singleton模版详解  http://www.cnblogs.com/fullsail/archive/2013/01/03/2842618.html








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-Programer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值