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 }
当然在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 }
懒汉式初始化
很多人觉得什么时候调用是用户的权力,不应该加以限制,所以就有了懒汉式方式初始化资源,在用到的时候如果没有初始化单例则初始化,如果初始化了则直接使用。
所以这种方式我们要加锁,防止资源被重复初始化。
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 }
但是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 }