zoukankan      html  css  js  c++  java
  • 七、单例设计模式共享数据分析、解决、call_once

    一、设计模式大概谈

    代码的一些写法,与常规的写法不太一样,程序灵活,维护起来很方便,但是别人接管、阅读代码很痛苦。

    用设计模式理念写出来的代码很晦涩。<< head first>>

    老外应付特别大的项目时候,把项目开发经验、模块划分经验,总结成设计模式。

    二、单例设计模式

    使用频率高。

    单例:整个项目中,有某个特殊或某些特殊的类,属于该类的对象,我只能创建1个,多了我就创建不了了。

    单例类(构造函数为private);

     1 class A{//单例类
     2 private:
     3     A() {} //私有化构造函数,就不能用A a;这种方式来创建对象了
     4     static A* m_instance;//静态成员变量
     5 public:
     6     static A* GetInstance(){
     7         if(m_instance == NULL){
     8             m_instance = new A();
     9             static huishou cl;
    10             
    11         }
    12         return m_instance;
    13     }
    14     
    15     class huishou{//类中套类,用来释放对象
    16     public:
    17         ~huishou(){
    18             if(A::m_instance){//如果这个m_instance不是空,那么就得释放A对象
    19             delete A::m_instance;
    20             A::m_instance=NULL;
    21             }
    22         }
    23     }
    24     
    25 };
    26 
    27 //静态变量初始化
    28 A* A::m_instance=NULL;
    29 
    30 A *p_a = A::GetInstance();//第一次调用函数,返回m_instance就非空了,当再次创建就不行了。

    三、单例设计模式共享数据问题分析、解决

    有时候需要在我们自己创建的线程(不是主线程)中创建A这个单例类的对象,这种线程可能最少2个。我们可能会面临

    GetInstance()这个成员函数要互斥,例如:

     1 using namespace std;
     2 std::mutex resource_mutex;
     3 
     4 
     5 class A{//单例类
     6 private:
     7     A() {} //私有化构造函数,就不能用A a;这种方式来创建对象了
     8     static A* m_instance;//静态成员变量
     9 public:
    10     static A* GetInstance(){
    11         std::unique_lock<std::mutex> mymutex(resource_mutex);//会自动加锁
    12         if(m_instance == NULL){
    13             m_instance = new A();
    14             static huishou cl;
    15             
    16         }
    17         return m_instance;
    18     }
    19     
    20     class huishou{//类中套类,用来释放对象
    21     public:
    22         ~huishou(){
    23             if(A::m_instance){//如果这个m_instance不是空,那么就得释放A对象
    24             delete A::m_instance;
    25             A::m_instance=NULL;
    26             }
    27         }
    28     }
    29     
    30 };
    31 
    32 //静态变量初始化
    33 A* A::m_instance=NULL;
    34 
    35 //线程入口函数
    36 void mythread(){
    37     cout << "thread is begining!" <<endl;
    38     A* p_a = A::GetInstance();//这里会出问题
    39     cout << "this thread over" << endl;
    40     return;
    41 }
    42 
    43 int main(){
    44     std::thread thread1(mythread);
    45     std::thread thread2(mythread);
    46     thread1.join();
    47     thread2.join();
    48     return 0;
    49     
    50 }

    两个线程的入口函数是相通的,有一种情况可能会发生:当thread1正好创建A对象(正在执行GetInstance)的时候任务被切换到了thread2,那么thread2也会同时执行到GetInstance,相当于两个线程同时执行这个函数,这就坏事了。所以要来一个锁,就行了。

    上面的效率太低了,相当于每次创建A都要调用get函数,还要锁上,相当于你这么复杂的步骤,只是为了解决初始化时的问题,这几很低效率了。

    怎样高效解决呢?看下面代码:

     1     static A* GetInstance(){
     2         //双重锁定提高效率
     3         if(m_instance==NULL)//双重锁定,双重检查
     4         {
             //这段函数也就是只执行一次
    5 std::unique_lock<std::mutex> mymutex(resource_mutex);//会自动加锁 6 if(m_instance == NULL){ 7 m_instance = new A(); 8 static huishou cl; 9 } 10 } 11 return m_instance; 12 }

    如何提高效率的?

    我们知道如果if(m_instance!=NULL)条件成立,表示肯定已m_instance已经被new过了;

    如果if(m_instance==NULL),不代表m_instance一定没有被new过,比如上面说的那种特殊情况。

    第一个if条件成功的情况就是第一次创建A对象或者几个线程同时第一次创建A对象的手,才会进入第一个if执行语句中,然后就加锁。。。。。。

    一旦这个对象已经创建了,那么第一个if里面的执行语句就根本不会再执行了了。也即是说,第一个if就专门是针对出创A对象或多个线程初创A对象的情况,一旦A对象有了,就再也不会执行里面的语句了,效率高了很多。

    四、std::call_once()

    函数模板

    能够保证函数a只被调用一次。

    具备互斥量这种能力,效率上比互斥量消耗更少。

    需要与一个标记结合使用,这个标记std::once_flag是一个结构。

    根据这个标记来决定对应的函数a是否被执行,调用call_once成功后,call_once()就把这个标记设置为一种已调用状态,对应的a函数就不会再被执行了。

     1 using namespace std;
     2 std::once_flag m_flag;//系统定义的标记
     3 
     4 class A{//单例类
     5     static void creatInstance(){//只被调用一次,不加说明都默认成是private
     6         m_instance = new A();
     7         static huishou cl;
     8     }
     9 private:
    10     A() {} //私有化构造函数,就不能用A a;这种方式来创建对象了
    11     static A* m_instance;//静态成员变量
    12 public:
    13     static A* GetInstance(){
    14         //如果两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕creatInstance()
    15         std::call_once(m_flag,creatInstance);
    16         return m_instance;
    17     }
    18     
    19     class huishou{//类中套类,用来释放对象
    20     public:
    21         ~huishou(){
    22             if(A::m_instance){//如果这个m_instance不是空,那么就得释放A对象
    23             delete A::m_instance;
    24             A::m_instance=NULL;
    25             }
    26         }
    27     }
    28     
    29 };
    30 
    31 //静态变量初始化
    32 A* A::m_instance=NULL;
    33 
    34 //线程入口函数
    35 void mythread(){
    36     cout << "thread is begining!" <<endl;
    37     A* p_a = A::GetInstance();//这里会出问题
    38     cout << "this thread over" << endl;
    39     return;
    40 }
    41 
    42 int main(){
    43     std::thread thread1(mythread);
    44     std::thread thread2(mythread);
    45     thread1.join();
    46     thread2.join();
    47     return 0;
    48     
    49 }

    PS:一般建议在主线程中创建单例对象

  • 相关阅读:
    全局变量与全局静态变量的区别:
    Python模块学习 ---- datetime
    python sys.path用法
    过来人谈《去360还是留在百度?》
    [编码问题] Python错误: SyntaxError: Non-ASCII character
    E513: write error, conversion failed (make 'fenc' empty to override)"解决办法
    巴真的点评
    set之hashset与TreeSet、LinkedHashSet实现原理
    list之linedlist与arraylist实现原理
    统一会话与单点登录
  • 原文地址:https://www.cnblogs.com/pacino12134/p/11240699.html
Copyright © 2011-2022 走看看