C++11并发与多线程笔记(7)单例设计模式共享数据分析、解决,call_once

文章目录

1、设计模式大概谈

设计模式

  • 代码的一些写法(这些写法跟常规写法不怎么一样),这样代码写出来的程序灵活,维护起来可能方便,但是别人接管,阅读代码非常痛苦。
  • 用“设计模式”理念写出来的代码是很晦涩的;
  • 当应付特别大的项目的时候,把项目的开发经验、模块划分经验,总结整理成设计模式(先有开发需求,后有理论总结和整理)。
  • 设计模式肯定有它独特的优点,要活学活用,不要深陷其中,生搬硬套。

2、单例设计模式

  • 单例设计模式,使用的频率比较高;
  • 整个项目中,有某个或者某些特殊的类,属于该类的对象,只能创建一个,多了创建不了。

示例代码:

#include <iostream>
using namespace std;

class MyCAS
{
private:
	MyCAS() {}	//私有化构造函数

private:
	static MyCAS* m_instance;	//静态成员变量

public:
	static MyCAS* GetInstance()
	{
		if (m_instance == NULL)
		{
			m_instance = new MyCAS();
			static CGarhuishou cl;
		}
		return m_instance;
	}

	class CGarhuishou	//类中套类,用来释放对象
	{
	public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance)
			{
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};

	void func()
	{
		cout << "测试" << endl;
	}
};

//类静态变量初始化
MyCAS* MyCAS::m_instance = NULL;

int main()
{
	MyCAS* p_a = MyCAS::GetInstance();		//创建一个对象,返回该类(MyCAS)对象的指针
	MyCAS* p_b = MyCAS::GetInstance();
	p_a->func();
	MyCAS::GetInstance()->func();

	return 0;
}

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

  • 面临的问题:需要在我们自己创建的线程(而不是主线程)中来创建MyCAS这个单例类的对象,这种线程可能不止1个(至少2个)。
  • 我们可能会面临GetInstance()这种成员函数要互斥。
  • 采用双重锁定来提升效率,这样就不用每次进GetInstance()函数都要加一次锁,解一次锁。

示例代码:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::mutex resource_mutex;
class MyCAS
{
private:
	MyCAS() {}	//私有化构造函数

private:
	static MyCAS* m_instance;	//静态成员变量

public:
	static MyCAS* GetInstance()
	{
		if (m_instance == NULL)		//双重锁定(双重检查)
		{
			std::unique_lock<std::mutex> mymutex(resource_mutex);
			if (m_instance == NULL)
			{
				m_instance = new MyCAS();
				static CGarhuishou cl;
			}
		}
		return m_instance;
	}

	class CGarhuishou	//类中套类,用来释放对象
	{
	public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance)
			{
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};

	void func()
	{
		cout << "测试" << endl;
	}
};

//类静态变量初始化
MyCAS* MyCAS::m_instance = NULL;

void mythread()
{
	cout << "我的线程开始执行了" << endl;
	MyCAS* p_a = MyCAS::GetInstance();
	cout << "我的线程执行完毕了" << endl;
}

int main()
{
	std::thread mytobj1(mythread);
	std::thread mytobj2(mythread);
	/*虽然这两个线程是同一个入口函数,
	但大家千万要记住,这是两个线程,
	所以这里会有两个流程(两条通路)同时开始执行mythread这个函数
	*/
	mytobj1.join();
	mytobj2.join();

	return 0;
}

4、std::call_once()

std::call_once()

  • c++11引入的函数,该函数的第二个参数是一个函数名a()
  • call_once()功能是能够保证函数a()只被调用一次。
  • call_once()具备互斥量这种能力,而且效率上,比互斥量消耗的资源更少;
  • call_once()需要与一个标记结合使用,这个标记是std::once_flagonce_flag是一个结构。
  • call_once()就是通过这个标记来决定对应的函数a()是否执行,调用call_once()成功后,call_once()就把这个标记设置为已调用状态。后续再次调用call_once(),只要once_flag被设置为了“已调用”状态,那么对应的函数a()就不会再执行了。

示例代码:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::mutex resource_mutex;
std::once_flag g_flag;	//系统定义的标记

class MyCAS
{
	static void CreatInstance()	//只被调用一次
	{
		std::chrono::milliseconds dura(20000);		//休息20s
		std::this_thread::sleep_for(dura);
		cout << "CreatInstance()被执行了" << endl;

		m_instance = new MyCAS();
		static CGarhuishou cl;
	}

private:
	MyCAS() {}	//私有化构造函数

private:
	static MyCAS* m_instance;	//静态成员变量

public:
	static MyCAS* GetInstance()
	{
		std::call_once(g_flag, CreatInstance);	//两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕CreatInstance()
		cout << "call_once()执行完毕" << endl;
		
		return m_instance;
	}

	class CGarhuishou	//类中套类,用来释放对象
	{
	public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance)
			{
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};

	void func()
	{
		cout << "测试" << endl;
	}
};

//类静态变量初始化
MyCAS* MyCAS::m_instance = NULL;

void mythread()
{
	cout << "我的线程开始执行了" << endl;
	MyCAS* p_a = MyCAS::GetInstance();
	p_a->func();
	cout << "我的线程执行完毕了" << endl;
	return;
}

int main()
{
	std::thread mytobj1(mythread);
	std::thread mytobj2(mythread);
	/*虽然这两个线程是同一个入口函数,
	但大家千万要记住,这是两个线程,
	所以这里会有两个流程(两条通路)同时开始执行mythread这个函数
	*/
	mytobj1.join();
	mytobj2.join();

	return 0;
}
  • 建议还是在主函数(主线程)中先创建好单例对象,然后再创建子线程。
  • std::call_once的效率实际上比双重锁定要低,还是建议使用双重锁定。

注:本人学习c++多线程视频地址:C++多线程学习地址

上一篇:单例模式 详解


下一篇:面向对象基础2