线程池
一、基本原理
线程池采用了预创建技术,提前创建好一定数量的线程,有新的任务到来时直接处理对应的任务。
线程池主要包括了三部分:
任务队列、工作线程、管理者线程
- 任务队列,存储需要处理的任务,由工作的线程来处理这些任务
(1)通过线程池提供的API函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除;
(2)已处理的任务将被删除;
(3)线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程。
- 工作线程(任务队列中任务的消费者),N个
(1)线程池维护了一定数量的工作线程,它们的作用是不断的读取任务队列,从里边取出任务并处理;
(2)如果任务队列为空,工作线程将会被阻塞(使用条件变量或信号量阻塞);
(3)如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作;
- 管理者线程(不处理任务队列中的任务),1个
(1)它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测;
(2)当任务过多的时候,可以适当的创建一些新的工作线程;
(3)当任务过少的时候,可以适当的销毁一些工作线程;
大致过程如下:
二、为什么要有线程池
(1)降低资源消耗。通过重复利用已创建好的线程来降低线程创建和销毁时给系统带来的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即得到处理。
(3)提高线程的可管理性。我们可以对线程池里的线程进行统一的分配,调优和监控。
问题:创建的线程越多性能越高,对吗?
答:不对。线程数量越多,可能会导致线程切换越频繁, 进而还有可能导致程序运行效率降低。多线程程序的运行效率, 呈现为正态分布, 线程数量从最开始的1开始逐渐增加, 程序的运行效率也逐渐变高, 直到线程数量达到一个临界值, 然后再次增加线程数量时, 程序的运行效率会减小(主要是由于频繁地线程切换影响到了整体线程运行效率)。
三、模拟实现线程池
01 成员变量
线程池的成员变量:
- 任务队列,quueu q;
- 线程个数,int num;
- 互斥量,pthread_mutex_t lock;
- 条件变量,pthread_cond_t cond;
02 代码实现
- Makefile文件
threadpool:threadpool.cc
g++ $^ -o $@ -lpthread
clean:
rm -f threadpool
- Task.hpp
#pragma once
#include<iostream>
#include<stdio.h>
using namespace std;
class Task
{
private:
int x;int y;
char op;
public:
Task(int _x,int _y,char _op)
:x(_x)
,y(_y)
,op(_op){ }
Task()
{};
void Run()
{
int ret=0;
switch(op){
case '+':
ret=x+y;
break;
case '-':
ret=x-y;
break;
case '*':
ret=x*y;
break;
case '/':
ret=x/y;
break;
case '%':
ret=x%y;
break;
}
printf("%d %c %d = %d\n",x,op,y,ret);
}
};
- threadpool.hpp
#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>
using namespace std;
#define NUM 4
template<class T>
class ThreadPool
{
private:
queue<T> q;//任务队列
int thread_num;//线程池的线程数量
pthread_mutex_t lock;//互斥锁
pthread_cond_t cond;//条件变量
public:
ThreadPool(int num=NUM)//初始化变量
:thread_num(num){
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);
}
bool Empty()
{
return q.size()==0?true:false;
}
static void* Routine(void* arg)//线程执行流
{
pthread_detach(pthread_self());//线程分离
ThreadPool* self=(ThreadPool*)arg;
while(1)
{
self->LockQueue();
while(self->Empty())//任务队列是否为空
{
self->Wait();
}
T data;
self->Pop(data);//取出任务
self->UnlockQueue();
cout<<pthread_self()<<"# ";
//处理任务
data.Run();//处理任务
sleep(1);
}
}
void Wait()//线程等待
{
pthread_cond_wait(&cond,&lock);
}
void LockQueue()//锁住队列
{
pthread_mutex_lock(&lock);
}
void UnlockQueue()//解锁队列
{
pthread_mutex_unlock(&lock);
}
void ThreadPoolInit()
{
pthread_t tid;
for(int i=0;i<thread_num;i++)//创建线程池中的线程
{
pthread_create(&tid,NULL,Routine,(void*)this);
}
}
void Push(const T& in)//将任务推进任务队列中
{
LockQueue();
q.push(in);
UnlockQueue();
SignalThread();
}
void SignalThread()//唤醒cond变量下的线程
{
pthread_cond_signal(&cond);
}
void Pop( T& out)//取出任务队列中的任务
{
out=q.front();
q.pop();
}
~ThreadPool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
};
- threadpool.cc文件
#include"threadpool.hpp"
#include"Task.hpp"
#include<time.h>
#include<stdlib.h>
int main()
{
ThreadPool<Task> * q=new ThreadPool<Task>();//创建线程池
q->ThreadPoolInit();
srand((long int)time(NULL));
while(1) //主线程往任务队列中放任务
{
char arr[]="+-*/%";
int x=rand()%100+1;
int y=rand()%100+1;
char op=arr[rand()%5];
Task t(x,y,op);//创建任务
q->Push(t);//将任务推送给队列中
}
return 0;
}
运行结果: