前面我们介绍了线程相关的所有概念,根据之前的所学的概念,接着我们看一下下面这张图
- 线程生命周期,一个线程从生到死的过程
线程池
以上介绍我们可以看出,在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在C++中,内存资源是及其宝贵的,计算机的大佬们就提出了线程池的概念。
- 线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
- 线程池避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
线程池的应用场景
- 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。
四种常见的线程池
- CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。
- SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
- SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。
- FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程
C++编写一个线程池
该线程池可以完成简单的计算器功能,加减乘除等,该线程池暂时设置的是5个线程可同时执行计算结果。
- threadpool.hpp
#ifndef __THREADPOOL_HPP__
#define __THREADPOOL_HPP__
#include <iostream>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <queue>
#include <unistd.h>
using namespace std;
typedef int (*HandlerTask_t)(int x,int y,int op);
class Task{
private:
int x;
int y;
char op; //0+,1-,2*,3/
HandlerTask_t handler;
public:
Task(int x_ = -1,int y_ = -1,int op_ = -1)
:x(x_),y(y_),op(op_)
{
}
void Register(HandlerTask_t handler_)
{
handler = handler_;
}
void Run()
{
int res = handler(x,y,op);
const char* op_ = "+-*/";
cout << "thread:[ "<<pthread_self() << " ]"<< " handler reslut:";
cout << x << op_[op] << y << " = " << res << endl;
}
~Task()
{}
};
class ThreadPool{
private:
int thread_nums;
int idle_nums;
queue<Task> task_queue;
pthread_mutex_t lock;
pthread_cond_t cond;
public:
void LockQueue()
{
pthread_mutex_lock(&lock);
}
void UnlockQueue()
{
pthread_mutex_unlock(&lock);
}
bool QueueIsEmpty()
{
return task_queue.size() == 0;
}
void ThreadIdle()
{
idle_nums++;
pthread_cond_wait(&cond,&lock);
idle_nums--;
}
void WakeupThread()
{
idle_nums--;
pthread_cond_signal(&cond);
idle_nums++;
}
void PopTask(Task& t)
{
t = task_queue.front();
task_queue.pop();
}
public:
ThreadPool(int num_ = 5):thread_nums(num_),idle_nums(0)
{
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);
}
static void* ThreadRotinue(void* arg)
{
pthread_detach(pthread_self());
ThreadPool *tp = (ThreadPool*)arg;
for(;;){
tp->LockQueue();
while(tp->QueueIsEmpty()){
tp->ThreadIdle();
}
Task t;
tp->PopTask(t);
//Task t = task_queue.front();
//task_queue.pop();
tp->UnlockQueue();
t.Run();
}
}
void InitThreadPool()
{
pthread_t t;
for(auto i = 0; i < thread_nums;i++){
pthread_create(&t,NULL,ThreadRotinue,this);
}
}
void PushTask(const Task &t)
{
LockQueue();
task_queue.push(t);
WakeupThread();
UnlockQueue();
}
~ThreadPool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
};
#endif
#include "threadpool.hpp"
int cal(int x,int y,int op)
{
int ret = -1;
switch(op)
{
case 0:
ret = x + y;
break;
case 1:
ret = x - y;
break;
case 2:
ret = x * y;
break;
case 3:
ret = x / y;
break;
default:
cout << "cal error!" << endl;
break;
}
}
int main()
{
ThreadPool tp;
tp.InitThreadPool();
srand((unsigned long)time(NULL));
for(;;){
sleep(1);
int x = rand() % 100 + 1;
int y = rand() % 100 + 1;
int op = rand() % 4;
Task t(x,y,op);
t.Register(cal);
tp.PushTask(t);
}
return 0;
}
- Makefile
threadpool:threadpool.cc
g++ -std=c++11 -o $@ $^ -lpthread
.PHONY:clean
clean:
rm -f threadpool