线程池概念
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
*线程池的应用场景:
1.需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2.对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。
- 线程池的种类:
- 线程池示例:
-
- 创建固定数量线程池,循环从任务队列中获取任务对象,
-
- 获取到任务对象后,执行任务对象中的任务接口
说白了就是预先创建若干个线程等待任务,省去了一个一个创建的时间
- 获取到任务对象后,执行任务对象中的任务接口
模拟线程池
本质上和我们原来写过的消费者生产者模型类似,只不过把创建线程放到了类中,一次性创建多个线程,相当于模拟了内存池。
.hpp
#pragma once
#include <iostream>
using namespace std;
#include <pthread.h>
#include <queue>
#include <unistd.h>
namespace ns_thread
{
const int default_num = 5;
pthread_mutex_t mtx;
pthread_cond_t c_cnd;
template <class T>
class ThreadPool
{
private:
//线程池中线程个数
int _num;
queue<T> task_queue;
public:
void Lock()
{
pthread_mutex_lock(&mtx);
}
void Unlock()
{
pthread_mutex_unlock(&mtx);
}
void Wait()
{
pthread_cond_wait(&c_cnd, &mtx);
}
void WakeUp()
{
pthread_cond_signal(&c_cnd);
}
bool IsEmpty()
{
return task_queue.empty();
}
public:
ThreadPool(int num = default_num)
: _num(num)
{
pthread_mutex_init(&mtx, nullptr);
pthread_cond_init(&c_cnd, nullptr);
}
// 在类中要让线程执行类内成员方法,是不可行的!!因为会有隐藏this指针就不符合创建线程的格式了
// 必须让线程执行静态方法,静态成员函数无法访问私有属性
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->Pop(&t);
tp->Unlock();
//这里先释放锁再进行任务处理,能达到一个线程处理任务,另一个线程可以抢锁获取数据,达到了并行的效果。
//如果先处理任务再释放锁,那么每个线程处理任务就是串行的,效率肯定没有并行的高
t.Run();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < _num; i++)
{
pthread_create(&tid, nullptr, Rountine, (void *)this);
}
}
void Push(const T &in)
{
Lock();
task_queue.push(in);
Unlock();
//唤醒线程处理任务
WakeUp();
}
void Pop(T *out)
{
*out = task_queue.front();
task_queue.pop();
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx);
pthread_cond_destroy(&c_cnd);
}
};
}
.cpp
#include<iostream>
#include"thread_pool.hpp"
#include"task.hpp"
#include<time.h>
using namespace std;
using namespace ns_thread;
using namespace ns_task;
int main()
{
srand((long long)time(nullptr));
ThreadPool<Task> *tp = new ThreadPool<Task>();
tp->InitThreadPool();
while(true)
{
Task t(rand()%20+1, rand()%10+1, "+-*/%"[rand()%5]);
tp->Push(t);
sleep(1);
}
return 0;
}
task.hpp
#pragma once
#include<iostream>
using namespace std;
#include<pthread.h>
//实现+-*/%
namespace ns_task
{
class Task
{
private:
int _x;
int _y;
char _c;
public:
Task(){};
Task(int x, int y, char c)
: _x(x), _y(y), _c(c)
{
}
~Task(){};
void Run()
{
int res = 0;
switch (_c)
{
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<<_c<<_y<<" = "<<res<<endl;
}
};
}
具体逻辑为:
先提前创建一批线程,然后这些线程挂起等待
当接受任务时,唤醒在等待队列中的的线程,处理任务 。这里代码的细节要单拿出来强调一下: