当用户调用new等函数的时候,会向内核申请空间。每调用一次申请一次空间而且在空间不够的时候还会执行内存算法等,需要花费时间。因此在程序创建之初,OS索性直接将一大块内存分配给用户。至于什么时候释放,怎么用都由用户自己决定。这就是内存池的概念。
内存池的目的是为了提高效率,线程池也同理。
因此我们可以类比出线程池的概念,即提前创建一批线程,以便于随时处理任务。
一个线程池除了包含大量线程之外,还包含一个任务队列。有一个生产任务的线程将任务传入任务队列中,线程池中的线程从任务队列中拿到任务,并进行处理。
#include
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;
}
};
}
#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);
}
}
#include
#include
#include
#include
#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);
}
};
}
单例模式即只让对象在内存中存在一份,即一个类只定义一个对象,对于线程池来说只有一个线程池就够了。因此线程池的定义可以使用单例模式。
一般而言,需要采用单例模式的情景是:
1.语义上只需要一个对象。
2.该对象内部存在大量空间保存大量数据,若存在多份(或各种拷贝),内存中就存冗余数据。
template<class T>
class Singleton
{
static T* inst;
public:
static T* GetInstance()
{
if(inst==NULL)
{
inst=new T();
}
return inst;
}
};
懒汉式的做法是,当需要使用对象的时候再创建一个对象。
template<class T>
class Singleton
{
static T data;
public:
static T* GetInstance()
{
return &data;
}
};
饿汉式表现为,当创建这个类的时候,对象就已经创建好了,可以随时使用。
由于单例本身会在任何场景,任何环境下被调用。因此可能会导致Getinstance被重入而产生线程安全问题。
我们可以使用单例模式改写线程池的代码:
此时需要将构造函数设为私有,只能调用getinstance函数来获得类。
首先我们需要定义一个静态的线程池变量以及静态的线程池方法,我们使用懒汉式来实现。
当一个线程进入getinstance函数时,要创建变量,但是被切走了,此时其他线程进入,就会导致线程安全的问题,因此需要进行加锁的操作。
#include
#include
#include
#include
#include"Task.hpp"
using namespace ns_task;
namespace ns_threadpool
{
template<class T>
class ThreadPool
{
private:
static ThreadPool<T>* ins;
int num_;
queue<T> task_queue_;
pthread_mutex_t mtx;
pthread_cond_t cond;
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;
public:
static ThreadPool<T>* get_instance()
{
if(ins==nullptr)
{
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)
{
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);
}
};
template<class T>
ThreadPool<T>* ThreadPool<T>::ins=nullptr;
}
同时主函数调用静态方法创建线程池:
#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);
}
}