本章Gitee仓库:线程池、单例模式
文章目录
- 1. 池化技术简述
- 2. 线程池
- 3. 单例模式
- 3.1 单例模式特点
- 3.2 饿汉方式和懒汉方式
- 3.3 单例模式线程安全
C++中的STL
,当空间不够时,会自动扩容,这个并不是我们需要多少,它就扩多少,之前自己实现的时候,选择的是1.5倍或者2倍扩容,这样的好处就是可以在一定空间范围内减少调整空间的次数,申请空间的底层也是系统调用,这样就能减少系统调用所花费的时间,本质就是空间换时间。
之前也写个简易的进程池,只不过没时间写笔记,看之后有没有时间写对应的笔记,有兴趣的可以先看看这个:进程池(进程通信——管道使用)
系统内部有一个(n个)任务队列,多个线程去竞争式去队列里面拿任务,使用线程的执行流不断向线程池push
任务,然后线程池内部的线程来消化,这本质就是生产消费模型。
调用逻辑:
#pragma once
#include
#include
#include
#include
#include
#include
#include"Task.hpp"
static const int defaultNum = 5; //默认5个
struct threadInfo
{
pthread_t tid;
std::string name;
};
template<class T>
class threadPool
{
public:
void Lock()
{
pthread_mutex_lock(&mutex_);
}
void Unlock()
{
pthread_mutex_unlock(&mutex_);
}
void WakeUp()
{
if(exitSingnal_)
pthread_cond_broadcast(&cond_);
else
pthread_cond_broadcast(&cond_);
}
void ThreadSleep()
{
pthread_cond_wait(&cond_, &mutex_);
}
bool IsEmpty()
{
return tasks_.empty();
}
std::string GetName(pthread_t tid)
{
for(auto &e: threads_)
{
if(e.tid == tid) return e.name;
}
return "None";
}
public:
static void *HandlerTask(void *args)
{
threadPool<T> *tp = static_cast<threadPool<T> *>(args);
std::string name = tp->GetName(pthread_self());
while(true)
{
tp->Lock();
while(tp->IsEmpty() && !tp->exitSingnal_)
{
tp->ThreadSleep();
}
if(tp->exitSingnal_)
{
//std::cout << " ?????" << std::endl;
//sleep(1);
tp->Unlock();
break;
}
T t = tp->Pop();
tp->Unlock();
t();
std::cout << "[thread " << name << "] is running," << " result:" << t.GetResult() << std::endl;
usleep(1);
//sleep(1);
}
}
void Push(const T &in)
{
Lock();
tasks_.push(in);
WakeUp();
Unlock();
}
T Pop()
{
T t = tasks_.front();
tasks_.pop();
return t;
}
void Start()
{
for(int i = 0; i < threads_.size(); i++)
{
threads_[i].name = std::to_string(i);
pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
}
}
void Join()
{
exitSingnal_ = true;
WakeUp();
for(int i = 0; i < threads_.size(); i++)
{
pthread_join(threads_[i].tid, nullptr);
}
}
int GetSurplus()
{
return tasks_.size();
}
public:
threadPool(int num = defaultNum)
:threads_(num),exitSingnal_(false)
{
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
~threadPool()
{
std::cout << "~threadPool" << std::endl;
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
//sleep(3);
}
private:
std::vector<threadInfo> threads_; //线程池
std::queue<T> tasks_; //任务队列
pthread_mutex_t mutex_;
pthread_cond_t cond_;
bool exitSingnal_;
};
HandlerTask
pthread_create()
要求线程执行的函数返回值为void*
,参数为void*
在类内,有一个
this
指针,设置成静态static
的即可。这里又会出现一个问题,静态成员无法直接访问类内成员,所以在传参的时候,直接传
this
指针过来
对于一些类,只允许实例化一个对象,这就叫做单例。
我们日常生活当中,我们吃完饭直接洗碗,然后下顿饭吃的时候就直接拿碗筷吃,这就是饿汉方式;
如果我们吃完饭,不洗碗,等到下次吃饭的时候,再洗碗用这些碗吃饭,这就懒汉方式。
而懒汉方式的核心思想就是"延时加载",这能够优化启动的速度。
以申请内存空间为例,我们向操作系统申请了一块内存空间,它只是允许我们访问,并不是直接加载到内存当中,而是在我们使用的时候,操作系统帮我们做缺页中断,然后再加载到内存,这本质上也是一种延时加载
延时加载整体上并没有提高效率,但是它在创建一个对象或者申请一块内存在申请和使用时间的配比发生了改变。
饿汉示例:
class Hungry
{
public:
Hungry()
{
std::cout << "Hungry man" << std::endl;
}
};
Hungry h1;
Hungry h2;
Hungry h3;
int main()
{
sleep(3);
std::cout << "main" << std::endl;
return 0;
}
饿汉就好比定义一个全局变量,全局变量在运行的时候就已经创建了,所以当全局变量较多的时候,势必会影响启动速度。
懒汉方式:
template <typename T>
class Singleton
{
static T *inst;
public:
static T *GetInstance()
{
if (inst == NULL)
{
inst = new T();
}
return inst;
}
};
当多个线程判断这个是否为空的时候,判断完被切走,这就会导致对象被new
成了多份,所以我们也需要对这个进行保护,既加锁。
全局或者静态初始化锁可以采用
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
懒汉线程池:
#pragma once
#include
#include
#include
#include
#include
#include
#include"Task.hpp"
static const int defaultNum = 5; //默认5个
struct threadInfo
{
pthread_t tid;
std::string name;
};
template<class T>
class threadPool
{
public:
void Lock()
{
pthread_mutex_lock(&mutex_);
}
void Unlock()
{
pthread_mutex_unlock(&mutex_);
}
void WakeUp()
{
if(exitSingnal_)
pthread_cond_broadcast(&cond_);
else
pthread_cond_broadcast(&cond_);
}
void ThreadSleep()
{
pthread_cond_wait(&cond_, &mutex_);
}
bool IsEmpty()
{
return tasks_.empty();
}
std::string GetName(pthread_t tid)
{
for(auto &e: threads_)
{
if(e.tid == tid) return e.name;
}
return "None";
}
public:
static void *HandlerTask(void *args)
{
threadPool<T> *tp = static_cast<threadPool<T> *>(args);
std::string name = tp->GetName(pthread_self());
while(true)
{
tp->Lock();
while(tp->IsEmpty() && !tp->exitSingnal_)
{
tp->ThreadSleep();
}
if(tp->exitSingnal_)
{
//std::cout << " ?????" << std::endl;
//sleep(1);
tp->Unlock();
break;
}
T t = tp->Pop();
tp->Unlock();
t();
std::cout << "[thread " << name << "] is running," << " result:" << t.GetResult() << std::endl;
usleep(1);
//sleep(1);
}
}
void Push(const T &in)
{
Lock();
tasks_.push(in);
WakeUp();
Unlock();
}
T Pop()
{
T t = tasks_.front();
tasks_.pop();
return t;
}
void Start()
{
for(int i = 0; i < threads_.size(); i++)
{
threads_[i].name = std::to_string(i);
pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
}
}
void Join()
{
exitSingnal_ = true;
WakeUp();
for(int i = 0; i < threads_.size(); i++)
{
pthread_join(threads_[i].tid, nullptr);
}
}
int GetSurplus()
{
return tasks_.size();
}
//获取单例
//静态方法,访问静态成员
static threadPool<T> *GetInstance()
{
//2次判断
//只有第一次创建对象会引发并发冲突问题,之后都是不符合的,所以之后并不需要判断,不用再做重复无效工作
if(tp_ == nullptr)//相当于一个开关
{
pthread_mutex_lock(&lock_);
if (tp_ == nullptr)
{
tp_ = new threadPool<T>();
}
pthread_mutex_unlock(&lock_);
}
return tp_;
}
//构造方法设置成私有
//凡是可能创建临时变量的,设置为私有
private:
threadPool(int num = defaultNum)
:threads_(num),exitSingnal_(false)
{
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
~threadPool()
{
std::cout << "~threadPool" << std::endl;
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
//sleep(3);
}
threadPool(const threadPool<T>&) = delete;
const threadPool<T>& operator=(const threadPool<T>&) =delete;
private:
std::vector<threadInfo> threads_; //线程池
std::queue<T> tasks_; //任务队列
pthread_mutex_t mutex_;
pthread_cond_t cond_;
bool exitSingnal_;
static threadPool<T> *tp_;
static pthread_mutex_t lock_;
};
//
template<class T>
threadPool<T> *threadPool<T>::tp_ = nullptr; //静态成员在类外初始化
template<class T>
//全局或者静态采用PTHREAD_MUTEX_INITIALIZER
pthread_mutex_t threadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;