zoukankan      html  css  js  c++  java
  • pthread_once重塑singleton模式

    单件模式是非线程安全的:

    // Single threaded version
    class Foo {
        private Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                helper = new Helper();
            }
            return helper;
        }
     
        // other functions and members...
    }


    这段在使用多线程的情况下无法正常工作。在多个线程同时调用getHelper()时,必须要获取锁,否则,这些线程可能同时去创建对象,或者某个线程会得到一个未完全初始化的对象。
    锁可以通过代价很高的同步来获得,就像下面的例子一样。

    // Correct but possibly expensive multithreaded version
    class Foo {
        private Helper helper = null;
        public synchronized Helper getHelper() {
            if (helper == null) {
                helper = new Helper();
            }
            return helper;
        }
     
        // other functions and members...
    }



    只有getHelper()的第一次调用需要同步创建对象,创建之后getHelper()只是简单的返回成员变量,而这里是无需同步的。 由于同步一个方法会降低100倍或更高的性能[2], 每次调用获取和释放锁的开销似乎是可以避免的:一旦初始化完成,获取和释放锁就显得很不必要。许多程序员一下面这种方式进行优化:
    检查变量是否被初始化(不去获得锁),如果已被初始化立即返回这个变量。
    获取锁
    第二次检查变量是否已经被初始化:如果其他线程曾获取过锁,那么变量已被初始化,返回初始化的变量。
    否则,初始化并返回变量。

    // Broken multithreaded version
    // "Double-Checked Locking" idiom
    class Foo {
        private Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null) {
                        helper = new Helper();
                    }
                }
            }
            return helper;
        }
     
        // other functions and members...
    }



    直觉上,这个算法看起来像是该问题的有效解决方案。然而,这一技术还有许多需要避免的细微问题。例如,考虑下面的事件序列:
    线程A发现变量没有被初始化, 然后它获取锁并开始变量的初始化。
    由于某些编程语言的语义,编译器生成的代码允许在线程A执行完变量的初始化之前,更新变量并将其指向部分初始化的对象。
    线程B发现共享变量已经被初始化,并返回变量。由于线程B确信变量已被初始化,它没有获取锁。如果在A完成初始化之前共享变量对B可见(这是由于A没有完成初始化或者因为一些初始化的值还没有穿过B使用的内存(缓存一致性)),程序很可能会崩溃。
    以上内容出自伟大的维基百科:http://zh.wikipedia.org/zh/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F

    使用pthread_once语义可以解决上述问题:The  purpose  of  pthread_once(pthread_once_t *once_control, void (*init_routine) (void))  is  to ensure that a piece of initialization code is executed at most once. The once_control argument points to a static or extern variable statically initialized to PTHREAD_ONCE_INIT.The first time pthread_once is called with a given once_control argument, it calls init_routine with  no  argument  and  changes  the value  of  the once_control variable to record that initialization has been performed. Subsequent calls to pthread_once with the sameonce_control argument do nothing.

    线程安全的例子:

    #include<iostream>
    #include<pthread.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<boost/noncopyable.hpp>
    using namespace std;
    using namespace boost;
    template<typename T>
    class singleton:noncopyable{
        public:
            static T& instance(){
                pthread_once(&ponce,&singleton::init);//第一次调用才会执行init,此后将改变ponce并将已经执行记录存入ponce
                return *value;
            }
        private:
            singleton();
            ~singleton();
            static void init(){
                value=new T();//这里只能调用T的默认构造函数,若要用户指定T的构造方式,可能需要模板特化...不怎么熟悉...汗
            }
        private:
            static pthread_once_t ponce;
            static T* value;
    };
    template<typename T>//静态成员类外初始化
    pthread_once_t singleton<T>::ponce=PTHREAD_ONCE_INIT;//ponce初始化
    template<typename T>
    T* singleton<T>::value=NULL;
    
    
    class test{//测试对象
        public:
            void show(){
                cout<<"show()"<<endl;
            }
    };
    int main(){
        test& p=singleton<test>::instance();//注意使用方法
        p.show();
        test& q=singleton<test>::instance();
        if(&p==&q)
            cout<<"singleton success"<<endl;
        else
            cout<<"singleton failure"<<endl;
        return 0;
    }



    程序输出:
    show()
    singleton success

  • 相关阅读:
    【HANA系列】SAP HANA LEFT/RIGHT字符串截取
    【HANA系列】SAP HANA SQL REPLACE替换字符串
    【HANA系列】SAP HANA SQL获取某字符串的位置
    【HANA系列】SAP HANA SQL获取字符串长度
    【HANA系列】SAP HANA SQL从给定日期中获取月份
    【HANA系列】SAP HANA SQL从给定日期中获取分钟
    【HANA系列】SAP HANA SQL查找字符串位置
    【HANA系列】SAP HANA SQL截取字符串
    【HANA系列】SAP HANA SQL获取时间中的小时
    【HANA系列】SAP HANA SQL从给定日期中获取年份
  • 原文地址:https://www.cnblogs.com/pangblog/p/3402540.html
Copyright © 2011-2022 走看看