目录
一、内存池
当用户调用new等函数的时候,会向内核申请空间。每调用一次申请一次空间而且空间不够时还会执行内存算法等,需要花费时间。因此在程序创建之初,OS索性直接将一大块内存分配给用户。
内存池的目的是为了提高效率,线程池也同理。
因此我们可以类比处线程池的概念,即提前创建一批线程,以便于随时处理任务。
二、线程池的实现
一个线程池除了包含大量线程之外,还包含一个任务队列。有一个生产任务的线程将任务传入任务队列中,线程池中的线程从任务队列中拿到任务,并进行处理。
1.任务文件
cpp
#include<iostream>
using namespace std;
namespace ns_task
{
class Task
{
private:
int x_;
int y_;
char op_;
public:
Task(int x,int y,char op):x_(x),y_(y),op_(op)
{};
Task()
{};
int Run()
{
int res=0;
switch(op_)
{
case '+':
res=x_+y_;
break;
case '-':
res=x_-y_;
break;
case '*':
res=x_*y_;
break;
case '/':
res=x_/y_;
break;
case '%':
res=x_%y_;
break;
default:
cout<<"bug"<<endl;
break;
}
cout<<"当前任务正在被"<<pthread_self()<<"处理"<<x_<<op_<<y_<<"="<<res<<endl;
}
};
}
2.线程池
cpp
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>
#include"Task.hpp"
using namespace ns_task;
namespace ns_threadpool
{
template<class T>
class ThreadPool
{
private:
int num_;
queue<T> task_queue_;
pthread_mutex_t mtx;
pthread_cond_t cond;
public:
ThreadPool(int num=5):num_(num)
{
pthread_mutex_init(&mtx,nullptr);
pthread_cond_init(&cond,nullptr);
}
bool IsEmpty()
{
return task_queue_.empty();
}
void lock()
{
pthread_mutex_lock(&mtx);
}
void unlock()
{
pthread_mutex_unlock(&mtx);
}
void Wait()
{
pthread_cond_wait(&cond,&mtx);
}
void Wakeup()
{
pthread_cond_signal(&cond);
}
void PushTask(const T& in)
{
lock();
task_queue_.push(in);
unlock();
Wakeup();
}
void PopTask(T* out)
{
*out=task_queue_.front();
task_queue_.pop();
}
static void* Routine(void* args)
{
pthread_detach(pthread_self());
ThreadPool<T>* tp=(ThreadPool<T>*)args;
while(true)
{
tp->lock();
if(tp->IsEmpty())
{
tp->Wait();
}
T t;
tp->PopTask(&t);
tp->unlock();
t.Run();
}
}
void InitThreadPool()
{
pthread_t tid;
for(int i=0;i<num_;i++)
{
pthread_create(&tid,nullptr,Routine,(void*)this);
}
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx);
pthread_cond_destroy(&cond);
}
};
}
3.主函数
cpp
#include"thread_pool.hpp"
using namespace ns_threadpool;
int main()
{
ThreadPool<Task>* tp=new ThreadPool<Task>();
srand((long long)time(nullptr));
tp->InitThreadPool();
while(true)
{
int x=rand()%20+1;
int y=rand()%10+1;
char arr[]="+-*/%";
char op=arr[rand()%1+4];
Task t(x,y,op);
tp->PushTask(t);
sleep(1);
}
}
三、单例模式
单例模式即只让对象在内存中存在一份,即一个类之定义一个对象,对于线程池来说只有一个线程池就够了。因此线程池的定义可以使用单例模式。
一般而言,需要采用单例模式的情景是:
1.语义上只需要一个对象。
2.该对象内部存在大量空间保存大量数据,若存在多份(或各种拷贝),内存中就存在冗余数据。
1.懒汉模式
cpp
template<class T>
class Singleton
{
private:
Singleton() {}
Singleton(const Singleton<T>& a)=delete;
Singleton<T>& operator=(Singleton<T>& a)=delete;
static Singleton<T>* inst;
public:
static T* GetInstance()
{
if(inst==NULL)
{
inst=new T();
}
return inst;
}
};
template<class T>
Singleton<T>* Singleton<T>::inst=nullptr;
懒汉式的做法是,当需要使用对象的时候再创建一个对象。
2.饿汉模式
cpp
template<class T>
class Singleton
{
ptivate:
Singleton() {}
Singleton(const Singleton& a)=delete;
Singleton<T>& operator=(Singleton<T>& a)=delete;
static Singleton data;
public:
static Singleton<T>* GetInstance()
{
return &data;
}
};
template <class T>
Singleton<T> Singleton<T>::data=0;
饿汉模式表现为,当创建这个类的时候,对象已经创建好了,可以随时使用。
3.线程安全
由于单例本身会再任何场景,任何环境下被调用。因此可能会导致GetInstance被重入而产生线程安全问题。
我们可以使用单例模式改写线程池的代码:
此时需要将构造函数设为私有,只能调用GetInstance来获得类。
首先我们需要定义一个静态的线程池变量以及静态的线程池方法,我们使用懒汉模式来实现。
当一个线程进入GetInstance函数时,要创建变量,但是被切走了,此时其他线程进入,就会导致线程安全的问题,因此需要进行加锁的操作。
cpp
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>
#include "task.hpp"
using namespace lf_task;
namespace lf_threadpool
{
template <class T>
class ThreadPool
{
private:
ThreadPool(int num = 5)
: _num(num)
{
pthread_mutex_init(&mtx, nullptr);
pthread_cond_init(&cond, nullptr);
}
ThreadPool(const ThreadPool<T>& tp)=delete;
ThreadPool<T>& operator=(ThreadPool<T>& tp)=delete;
static ThreadPool<T>* ins;
public:
static ThreadPool<T>* get_instance()
{
static pthread_mutex_t Lock=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&Lock);
if(ins==nullptr)
{
ins=new ThreadPool<T>();
ins->InitThreadPool();
}
pthread_mutex_unlock(&Lock);
return ins;
}
bool IsEmpty()
{
return _task_queue.empty();
}
void lock()
{
pthread_mutex_lock(&mtx);
}
void unlock()
{
pthread_mutex_unlock(&mtx);
}
void Wait()
{
pthread_cond_wait(&cond, &mtx);
}
void Wakeup()
{
pthread_cond_signal(&cond);
}
void PushTask(const T &in)
{
pthread_mutex_lock(&mtx);
_task_queue.push(in);
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond);
}
void PopTask(T *out)
{
*out = _task_queue.front();
_task_queue.pop();
}
static void *Rountine(void *args)
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T> *)args;
while (true)
{
tp->lock();
while (tp->IsEmpty())
{
tp->Wait();
}
T t;
tp->PopTask(&t);
tp->unlock();
t.Run();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < _num; i++)
{
pthread_create(&tid, nullptr, Rountine, (void *)this);
}
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx);
pthread_cond_destroy(&cond);
}
private:
int _num;
queue<T> _task_queue;
pthread_mutex_t mtx;
pthread_cond_t cond;
};
template<class T>
ThreadPool<T>* ThreadPool<T>::ins=nullptr;
}
同时主函数调用静态方法创建线程池:
cpp
#include"thread_pool.hpp"
using namespace ns_threadpool;
int main()
{
srand((long long)time(nullptr));
while(true)
{
int x=rand()%20+1;
int y=rand()%10+1;
char arr[]="+-*/%";
char op=arr[rand()%5];
Task t(x,y,op);
ThreadPool<Task>::get_instance()->PushTask(t);
sleep(1);
}
}