七单例设计模式共享数据分析解决call_once

Posted pacino12134

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七单例设计模式共享数据分析解决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:一般建议在主线程中创建单例对象

 

以上是关于七单例设计模式共享数据分析解决call_once的主要内容,如果未能解决你的问题,请参考以下文章

线程安全的对象生命期管理

C++单例模式实现(线程安全&支持多参数构造)

单例模式实例

单例设计模式-共享数据问题

单例模式

双重检查锁定模式 - 在传递给 call_once 的 lambda 中捕获