C++11并发与多线程总结(四) --单例模式(Singleton)下的线程安全问题

文章目录

(一) 线程概念、创建及传参
(二) 独占互斥锁–mutex,lock_guardy与其他mutex
(三) unique_lock替换lock_guardy
(四) 单例模式(Singleton)下的线程安全问题
(五) window临界区
(六) condition_variable条件变量
(七) std::async异步任务与std::future< >
(八) packaged_task< >与promise< >
(九) 原子操作atomic<>简介

(1)单例模式
  1. 单例 Singleton 是设计模式的一种,其特点是只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到那个唯一实例
  2. 一般用于操作数据池,或配置属性封装成类
  3. 外部不可创建,创建后指向指针也不可释放

(2)多线程获取单例模式接口

1.若多个线程同时接口,则可能在单例初始化时多个线程同时创建了多个单例,导致内存泄漏
2.解决以上问题必须对其部分上锁,也需要考虑效率问题
3.双重判定解决(减少线程的堵塞等待)
4.std::call_once()类模板解决
5.call_once需要配合once_flag使用


  • call_once的使用
    1.首先定义一个std::once_flag flag标记,类似于锁
    2.创建初始化单例构造的成员函数
    3.在接口函数调用std::call_once()
    4.第一个参数传入flag,第二个参数传入函数名
std::once_flag g_flag;

void CreatSingleton()  //创建单例
{
	if (m_instance == nullptr)  
	{
		m_instance = new CSingleton();
		static AutoRelease autorelease;  //自动析构类
	}
}
CSingleton* CSingleton::GetInstance()
{

	std::call_once(g_flag, CreatSingleton); //多个线程进入则等待,一个线程创建,相当于std::once_flag g_flag的锁,创建后其他线程退出,后面不会再调用

	return m_instance;
}

  • 源码如下
#include 
#include
#include

using namespace std;

class CSingleton
{
public:
	static CSingleton* GetInstance();

	void run()
	{
		m_mutexdata.lock();
		m_count++;
		m_mutexdata.unlock();
		cout << "run()执行" << m_count << endl;
	}

private:
	CSingleton(); //私有构造函数防止被外部创建
	~CSingleton(); //私有析构函数防止外部使用 delete

	class AutoRelease  //自动析构类
	{
	public:
		AutoRelease() {};
		~AutoRelease() {
			delete m_instance;
			cout << "释放单例~" << endl;
			m_instance = nullptr;
		};

	private:

	};

private:
	static CSingleton* m_instance;

	static std::mutex* m_mutex;

	int m_count = 0;
	mutex m_mutexdata; //保护data
};

CSingleton* CSingleton::m_instance = nullptr; //必须初始化
mutex* CSingleton::m_mutex = new mutex(); //必须初始化
CSingleton::CSingleton()
{
	cout << "CSingleton()创建" << endl;
}

CSingleton::~CSingleton()
{
	cout << "CSingleton()销毁" << endl;
}

CSingleton* CSingleton::GetInstance()
{
	if (m_instance == nullptr) //双重判定 提高效率(线程堵塞等待)
	{
		std::unique_lock<mutex> mylock(*m_mutex);//若没有这行则创建多个,测试最终data不为300

		if (m_instance == nullptr)  //若多个线程同时访问则会创建多个导致内存泄漏
		{
			m_instance = new CSingleton();
			static AutoRelease autorelease;
		}
	}

	//以上可用 void CreatSingleton() 完成new创建
	//std::call_once(g_flag, CreatSingleton); //多个线程进入则等待,一个线程创建 相当于std::once_flag g_flag的锁

	return m_instance;
}

void getSingleton()
{
	for (int i = 0; i < 100; i++)
	{
		CSingleton* p = CSingleton::GetInstance();
		p->run();
	}

}
int main()
{
	//CSingleton* p1 = CSingleton::GetInstance();
	//p1->run();

	////delete p1;  //无法通过编译

	//CSingleton* p2 = CSingleton::GetInstance();
	//p2->run();

	thread mythread1(getSingleton);
	thread mythread2(getSingleton);
	thread mythread3(getSingleton);
	mythread1.join();
	mythread2.join();
	mythread3.join();
}

你可能感兴趣的:(C++11并发与多线程总结)