C++线程安全的单例模式

实现C++线程安全的案例模式的三种方式:

  • 局部静态变量
  • 饿汉式初始化
  • 懒汉式初始化

局部静态变量(最常见)

我们知道在一个函数中定义一个静态局部变量,它只会初始化一次,就是在这个函数第一次调用的时候,以后无论调用几次,函数内的局部变量都不再初始化,并且所有对象的静态成员变量都共享同一块静态存储空间。

 1 #include <iostream>
 2 using namespace std;
 3 //C++11以前该方式存在风险,在多个线程初始化时可能会开辟多个实例
 4 //C++11之后,编译器经过优化了,不会在出现多个实例的问题,也是最简单的一种方式。
 5 class Single {
 6 private:
 7     Single() {
 8 
 9     }
10     Single(const Single&) = delete;     //禁止拷贝构造
11     Single& operator=(const Single&) = delete;     //禁止拷贝复制
12 public:
13     static Single& GetInst() {
14         static Single single;
15         return single;
16     }
17 };
18 void test_single() {
19     //多线程下可能出现问题
20     cout << "s1 address is" << &Single::GetInst() << endl;
21     cout << "s2 address is" << &Single::GetInst() << endl;
22 }
23 int main()
24 {
25     test_single();
26     return 0;
27 }
View Code

 

当然在C++11之前,其他人为了避免简单的静态局部变量实现单例模式带来的弊端

设计出了饿汉式初始化,和懒汉式初始化

饿汉式初始化

主线程启动之后,在其他线程没有启动之前,由主线程立马先初始化单例资源,这样其他线程就无法重复初始化了。

 1 #include <iostream>
 2 #include <thread>
 3 using namespace std;
 4 class SingleHungry {
 5 private:
 6     SingleHungry() {            //无参构造 
 7 
 8     }
 9     SingleHungry(const SingleHungry&) = delete;     //禁止拷贝构造 
10     SingleHungry& operator=(const SingleHungry&) = delete;     //禁止拷贝复制 
11 public:
12     static SingleHungry* GetInst() {
13         if (single == nullptr) {
14             single = new SingleHungry();
15         }
16         return single;
17     }
18 private:
19     static SingleHungry* single;
20 };
21 //饿汉式初始化
22 SingleHungry* SingleHungry::single = SingleHungry::GetInst();
23 static int index = 0;
24 void thread_fun() {
25     cout << "this is thread:" << index++ << endl;
26     cout << "address is:" << SingleHungry::GetInst() << endl;
27 }
28 void test_singlehungry() {
29     cout << "s1 address is:" << SingleHungry::GetInst() << endl;    //主线程
30     cout << "s2 address is:" << SingleHungry::GetInst() << endl;    //主线程
31     for (int i = 0; i < 3; i++)        //3个子线程
32     {
33         std::thread tid(thread_fun);
34         tid.join();
35     }
36 }
37 int main()
38 {
39     test_singlehungry();
40     return 0;
41 }
View Code

 

懒汉式初始化

很多人觉得什么时候调用是用户的权力,不应该加以限制,所以就有了懒汉式方式初始化资源,在用到的时候如果没有初始化单例则初始化,如果初始化了则直接使用。

所以这种方式我们要加锁,防止资源被重复初始化

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 using namespace std;
 5 //如果用农户使用懒汉式就需要加锁
 6 //但是回收指针同样存在问题,存在多重释放或者不知道哪个指针释放的问题。
 7 class SingleLazy {
 8 private:
 9     SingleLazy() {            //无参构造 
10 
11     }
12     SingleLazy(const SingleLazy&) = delete;     //禁止拷贝构造 
13     SingleLazy& operator=(const SingleLazy&) = delete;     //禁止拷贝复制 
14 public:
15     static SingleLazy* GetInst() {
16         if (single != nullptr) 
17         {
18             return single;    //已被初始化,直接返回
19         }
20         s_mutex.lock();
21         if (single != nullptr) 
22         {
23             s_mutex.unlock();
24             return single;
25         }
26         single = new SingleLazy();        //无法确定如何回收
27         s_mutex.unlock();
28         return single;
29     }
30 private:
31     static SingleLazy* single;
32     static std::mutex s_mutex;
33 };
34 //饿汉式初始化
35 SingleLazy* SingleLazy::single = nullptr;
36 std::mutex SingleLazy::s_mutex;
37 
38 void thread_fun(int i) {
39     cout << "this is lazy thread:" << i << endl;
40     cout << "address is:" << SingleLazy::GetInst() << endl;
41 }
42 void test_singlelazy() {
43     for (int i = 0; i < 3; i++)        //3个子线程
44     {
45         std::thread tid(thread_fun,i);
46         tid.join();
47     }
48     //合适释放new的对象?造成内存泄漏。
49 }
50 int main()
51 {
52     test_singlelazy();
53     return 0;
54 }
View Code

 但是new出来的对象,什么时候释放呢?

 

可以利用智能指针完成自动回收

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 using namespace std;
 5 //如果用户使用懒汉式就需要加锁
 6 //但是回收指针同样存在问题,存在多重释放或者不知道哪个指针释放的问题。
 7 class SingleAuto {
 8 private:
 9     SingleAuto() //无参构造 
10     {            
11 
12     }
13     SingleAuto(const SingleAuto&) = delete;     //禁止拷贝构造 
14     SingleAuto& operator=(const SingleAuto&) = delete;     //禁止拷贝复制 
15 public:
16     ~SingleAuto() 
17     {
18         cout << "Single auto delete success" << endl;
19     }
20     static std::shared_ptr<SingleAuto> GetInst() {
21         if (single != nullptr) 
22         {
23             return single;    //已被初始化,直接返回
24         }
25         s_mutex.lock();
26         if (single != nullptr) 
27         {
28             s_mutex.unlock();
29             return single;
30         }
31         single = std::shared_ptr<SingleAuto>(new SingleAuto);    //智能指针,自动回收内存
32         s_mutex.unlock();
33         return single;
34     }
35 private:
36     static std::shared_ptr<SingleAuto> single;
37     static std::mutex s_mutex;
38 };
39 //饿汉式初始化
40 std::shared_ptr<SingleAuto> SingleAuto::single = nullptr;
41 std::mutex SingleAuto::s_mutex;
42 
43 void thread_singleauto(int i) {
44     cout << "this is auto thread:" << i << endl;
45     cout << "address is:" << SingleAuto::GetInst() << endl;
46 }
47 void test_singleauto() {
48     for (int i = 0; i < 3; i++)        //3个子线程
49     {
50         std::thread tid(thread_singleauto,i);
51         tid.join();
52     }
53 }
54 int main()
55 {
56     test_singleauto();
57     return 0;
58 }
View Code

 

posted @ 2023-11-10 01:27  C++杀我  阅读(402)  评论(0编辑  收藏  举报