【Linux】线程安全的单例模式以及计算密集型线程和IO密集型线程

一.单例模式

1.含义

     一个类只能实例化出一个对象

2.单例模式实现的两种方式

  • 饿汉模式

        我们在程序运行之初就将对象创建好,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。是以空间换时间的做法。这样程序运行中想要调用这个实例时都可以直接使用。举个例子:就像是一个人在吃完饭后立刻就去将碗洗了,这样以便于在下一次开饭的时候可以马上吃到饭。

      实现原理:

          为了在运行前就实例化好对象,并且不允许其他方式来实例化对象。那么我们首先将构造函数/拷贝构造函数/赋值运算符重载函数定义为私有。然后在在定义一个函数返回单例的地址。切记这个函数必须定义为静态的。倘若不设置成静态的,那么我们只有创建了对象才能调他,但是我们的初衷又是不要通过别的方式创建对象,这样就会产生矛盾。

    饿汉模式的优点:

  1. 简单
  2. 因为我们在程序启动时就设置好了单例,所以饿汉模式是线程安全的。

    饿汉模式的缺点:

  1. 会拖慢程序启动的速度。
  2. 如果有多个单例类对象实例启动顺序不确定。
class Hungry
{
public:
	static Hungry* GetInstance()//对外提供一个静态的成员函数,返回我们的单例
	{
		return &onlyInstance;
	}

private:
	static Hungry onlyInstance;//定义一个静态的实例

private://将其他可能产生实例的函数都弄成私有,设置为删除函数是C++11的新写法
	Hungry() {};
	Hungry(const Hungry&) = delete;
	Hungry& operator=(const Hungry&) = delete;
	
};

Hungry Hungry::onlyInstance;//单例别忘了类外初始化

int main()
{
	Hungry* a = Hungry::GetInstance();
}
  • 懒汉模式

       在用到的时候才去创建单例。例如:一个人吃完饭在,并不会直接将碗洗了,只有在下一次吃饭的时候才去洗碗

       这种模式往往用在单利对象在创建时很耗时,占用资源,例如加载插件等等。这时候就会用到懒汉模式。

懒汉模式的优点:

扫描二维码关注公众号,回复: 5623667 查看本文章
  1. 第一次要使用单例的时候才去创建, 进程启动的时候无负载。
  2. 多个单例启动顺序可以自由控制多个单例启动顺序可以自由控制

懒汉模式缺点:

  1. 代码复杂
  2. 需要考虑线程安全,由用户加锁
#include<iostream>
#include<mutex>
#include<thread>
using namespace std;
class Lazy
{
public:
	static Lazy* GetInstance()//返回单例
	{
		if (OnlyInstance == nullptr)
		{
			m_mutex.lock();
			if (OnlyInstance == nullptr)//?为什么要双层检查
			{
				OnlyInstance = new Lazy();
			}
			m_mutex.unlock();
		}
		return OnlyInstance;
	}

private:
	static Lazy* OnlyInstance;//单例指针
	static mutex m_mutex;//互斥量
private:
	Lazy() {};
	Lazy(const Lazy&) {};
	Lazy& operator=(const Lazy&) {};
};

Lazy* Lazy::OnlyInstance = nullptr;//初始化
mutex Lazy::m_mutex;

 我们看到上边有层if判断那么为什么需要两层???

  • 首先说第一层检查:

当一个线程运行到第一层检查,如果发现实例已经创建,就直接返回。
如果没有创建则通过第一层检查。
进入第一层检查后,我们让各个线程竞争锁,竞争到锁的线程(我们用A表示)来进行第二层检查

  • 第二层检查:

当线程A获取锁后,再次判断,如果实例仍未被创建,则创建实例。如果实例已经被创建,那么就是上次拿到锁的线程(B)创建了实例(B创建的时候A一直阻塞在获得锁的地方,A并不知道B先于他获得了锁,如果不再次判断,那么仍然有可能创建多个实例)。返回即可。
 

二.计算密集型线程池

 cpu使用率较高(也就是一些复杂运算,逻辑处理),所以线程数一般只需要cpu核数的线程就可以了。 这一类型的在开发中多出现的一些业务复杂计算和逻辑处理过程中。 

数量一般为 N+1个   N为CPU核心数

三.IO密集型

cpu使用率较低,程序中会存在大量I/O操作占据时间,导致线程空余时间出来,所以通常就需要开cpu核数的两倍的线程, 当线程进行I/O操作cpu空暇时启用其他线程继续使用cpu,提高cpu使用率 通过上述可以总结出:线程的最佳数量: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目 线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。这一类型在开发中主要出现在一些读写操作频繁的业务逻辑中。 

数量一般为:2N + 1个  N为CPU核心数

猜你喜欢

转载自blog.csdn.net/alidada_blog/article/details/87446816