多线程学习总结(1):https://blog.csdn.net/hansionz/article/details/84665815
多线程学习总结(2):https://blog.csdn.net/hansionz/article/details/84675536
多线程学习总结(3):https://blog.csdn.net/hansionz/article/details/84766601
多线程学习总结(4):https://blog.csdn.net/hansionz/article/details/84842790
C++实现一个简单线程池
1.什么是线程池
线程池
是一种线程使用模式
。线程过多会带来调度开销
,进而影响缓存局部性
和整体性
能。而线程池维护着多个线程
, 等待着监督管理者分配可并发执行的任务
。这避免了在处理短时间任务时创建
与销毁
线程的代价。线程池不仅能够保证内核的充分利用
,还能防止过分调度
。可用线程数量应该取决于可用的并发处理器
、处理器内核、内存、网络sockets
等的 量。
线程池
,简单来说就是存在一堆
已经创建好的线程(最大数目一定)
,初始时他们都处于空闲状态
,当有新的任务进来,从线程池
中取出一个空闲的线程处理任务
,然后当任务处理完成
之后,该线程被重新放回到线程池
中,供其他的任务使用,当线程池中的线程都在处理任务
时,就没有空闲
线程供使用,此时,若有新的任务
产生,只能等待线程池
中有线程结束任务空闲才能执行,下面是线程池的工作原理图:
2.为什么要存在线程池
线程本身存在开销
,我们利用多线程
来进行任务处理
,单线程也不能滥用,无止境的开新线程会给系统产生大量消耗,而线程本来就是可重用的资源
,不需要每次使用时都进行初始化
,因此可以采用有限的线程
个数处理无限的任务
。
3.常见线程池应用场景
- 需要
大量的线程
来完成任务,且完成任务的时间比较短
。 例如WEB服务器
完成网页请求
这样的任务,使用线程池技术
是非常合适的。因为单个任务小
,而任务数量巨大
,可以想象一个热门网站的点击次数
。 但对于长时间的任务, 比如一个Telnet
连接请求,线程池的优点就不明显了。因为Telnet
会话时间比线程的创建时间长的多。 - 一些对
性能
要求苛刻的应用
,例如要求服务器迅速响应客户
请求,可以利用线程池技术
来减少线程创建和销毁
的开销。 - 一些接受
突发性
的大量请求,但不会
使服务器因此产生大量线程
的应用。突发性大量客户请求。在没有线程池
情况下,将产生大量线程,虽然理论上大部分操作系统
线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限
,出现内存错误。
4.线程池的实现
threadpool.hpp
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
using namespace std;
//函数指针,指向任务的处理方法
typedef int (*call)(int,int);
//封装一个任务处理类
class Task
{
public:
Task(int x, int y, call handler)
:_x(x)
,_y(y)
,_z(0)
,_handler(handler)
{}
//执行处理方法,保存结果
void Run()
{
_z = _handler(_x,_y);
}
//打印结果 for test
void Show()
{
cout << "thread:" << pthread_self() << ". Result is:" << _z << endl;
}
private:
int _x;
int _y;
int _z;//用来保存任务处理的结果
call _handler;
};
//线程池
class ThreadPool
{
private:
int thread_nums;//线程个数
queue<Task> t_queue;//任务队列
pthread_mutex_t lock;//互斥锁,保护临界资源
pthread_cond_t cond;//条件变量,实现同步
bool is_stop;//标志该线程池是否要停止
private:
static void *thread_routine(void *arg)
{
ThreadPool* tp = (ThreadPool*)arg;
//分离该线程,完成任务后不用join
pthread_detach(pthread_self());
while(1)
{
//使用互斥锁保护临界资源t_queue
tp -> LockQueue();
//使用while进行二次判断,防止假唤醒
while(tp -> IsEmpty())
tp -> IdleThread();
Task t = tp -> GetTask();
tp -> UnlockQueue();
t.Run();
t.Show();
}
}
//唤醒条件变量等待下的一个线程
void NotifyOneThread()
{
pthread_cond_signal(&cond);
}
//唤醒条件变量等待下的所有线程
void NotifyAllThread()
{
pthread_cond_broadcast(&cond);
}
public:
ThreadPool(int num)
:thread_nums(num)
,is_stop(false)
{}
//初始化线程池
void InitThread()
{
//初始化条件变量和互斥锁
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&cond, NULL);
//创建一批线程
for(int i = 0;i < thread_nums;i++)
{
pthread_t tid;
pthread_create(&tid, NULL, thread_routine, (void*)this);
}
}
//使用互斥量锁住任务队列
void LockQueue()
{
pthread_mutex_lock(&lock);
}
//解锁
void UnlockQueue()
{
pthread_mutex_unlock(&lock);
}
//判断任务队列是否为空
bool IsEmpty()
{
return t_queue.size() == 0 ? true : false;
}
//使得线程在条件变量下等待
void IdleThread()
{
if(is_stop)
{
UnlockQueue();
thread_nums--;
pthread_exit((void*)0);
cout << "thread:" << pthread_self() << "quit!" << endl;
return;
}
pthread_cond_wait(&cond, &lock);
}
//停止线程池
void Stop()
{
LockQueue();
is_stop = true;
UnlockQueue();
//线程的熟练如果大于0说明还存在线程在阻塞唤醒所有线程并停止
if(thread_nums > 0)
{
NotifyAllThread();
}
}
//向任务队列中添加任务
void AddTask(Task& t)
{
LockQueue();
if(is_stop)
{
UnlockQueue();
return;//本线程退出
}
t_queue.push(t);
NotifyOneThread();//实现同步
UnlockQueue();
}
//从任务队列中拿出一个任务
Task GetTask()
{
Task t = t_queue.front();
t_queue.pop();
return t;
}
//销毁条件变量和互斥量
~ThreadPool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
};
#endif //__T__THREAD_POOL_H__
#include "threadpool.hpp"
//线程池中线程的熟练
const int thread_num = 5;
//向任务队列中添加几个简单任务
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Div(int x, int y)
{
return x/y;
}
int Mul(int x, int y)
{
return x * y;
}
int main()
{
ThreadPool* tp = new ThreadPool(thread_num);
tp -> InitThread();//初始化线程池
int count = 1;
while(1)
{
sleep(1);
Task t(count, count - 1, Add);
tp -> AddTask(t);
Task t1(count, count - 1,Sub);
tp -> AddTask(t1);
Task t2(count, count, Mul);
tp -> AddTask(t2);
Task t3(count, count, Div);
tp -> AddTask(t3);
++count;
}
return 0;
}