目录
一丶什么是线程池
线程池是线程的一种使用模式。在前面的情况中,我们都是遇到任务然后创建线程再执行。但是线程的频繁创建就类似于内存的频繁申请,会给操作系统带来更大的压力,进而影响整体的性能。
所以我们一次申请好一定数量而定线程,然后将线程的管理操作交给线程池,就避免了在短时间内不断创建与销毁线程的代价,线程池不但能够保证内核的充分利用,还能防止过分调度,并根据实际业务情况进行修改。
二丶线程池的优点
- 任务来到立马就有线程去执行任务,节省了创建线程的时间。
- 防止服务器线程过多导致的系统过载问题
- 相对于进程池,线程池资源占用较少,但是健壮性很差
三丶线程池的应用
- 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技 术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情 况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误.
四丶线程池种类
- 创建固定数量线程池,循环从任务队列中获取任务对象
- 获取到任务对象后,执行任务对象中的任务接口
五丶线程池使用
pthreadpool.hpp
#include<iostream> #include<pthread.h> #include<queue> #include"task.hpp" #include<unistd.h> using namespace std; using namespace qzh1; namespace qzh { const int g_num = 5; template <class T> class ThreadPool { private: int num_; //固定大小的线程池 queue<T> task_queue_; //任务队列,使用STL的queue实现 pthread_mutex_t mtx_; //定义一把锁 pthread_cond_t cond_; //定义一个条件变量 public: void Lock() { pthread_mutex_lock(&mtx_);} //加锁操作 void Unlock() { pthread_mutex_unlock(&mtx_);} //解锁操作 bool IsEmpety() { return task_queue_.empty();} //判断任务队列是否为空 void Wait() { pthread_cond_wait(&cond_, &mtx_);} //让线程在条件变量下等待 void WakeUp() { pthread_cond_signal(&cond_);} //唤醒在条件变量下等待的线程 public: ThreadPool(int num = g_num):num_(num) { pthread_mutex_init(&mtx_, nullptr); pthread_cond_init(&cond_, nullptr); } //在类中要让线程执行类内成员方法,是不可行的 //必须让线程执行静态方法 static void* Rountine(void* args) { pthread_detach(pthread_self()); ThreadPool<T>* tp = (ThreadPool<T>*)args; while(true) { tp->Lock(); while(tp->IsEmpety()) { 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, Rountine, (void*)this); } } void PushTask(const T& in)//向任务队列添加任务 { Lock(); task_queue_.push(in); Unlock(); WakeUp(); } void PopTask(T* out)//从任务队列获取任务 { *out = task_queue_.front(); task_queue_.pop(); } ~ThreadPool() { pthread_mutex_destroy(&mtx_); pthread_cond_destroy(&cond_); } }; }
main.cc
#include "threadpool.hpp" #include "task.hpp" #include <ctime> #include <cstdlib> using namespace qzh; using namespace qzh1; int main() { ThreadPool<Task>* tp = new ThreadPool<Task>();//创建线程池 tp->InitThreadPool(); //进行初始化 srand((long long)time(nullptr));//生产随机数 while(true) //不断向任务队列塞数据 { Task t(rand() % 20 + 1, rand() % 10 + 1, "+-*/%"[rand() % 5]); tp->PushTask(t); sleep(1); } return 0; }
task.hpp
#pragma once #include <iostream> #include <pthread.h> using namespace std; namespace qzh1 { class Task { private: int x_; int y_; char op_;//用来表示:+ 、- 、* 、/ 、% public: Task(){} Task(int x, int y, char op):x_(x), y_(y), op_(op){} string show() { string message = to_string(x_); message += op_; message += to_string(y_); message += "=?"; return message; } 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; } printf("当前任务正在被:%lu处理,处理结果为:%d %c %d = %d\n",pthread_self(), x_, op_, y_, res); return res; } int operator()() { return Run(); } ~Task(){} }; }
六丶单例模式
单例(Singleton)模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例 ;
使用场景:
语义上只需要一个
该对象内部存在大量的空间,保存了大量的数据,如果允许该对象存在多份,或者允许发生各种拷贝,内存中存在冗余数据;
一般Singleton模式通常有三种形式:饿汉式:吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭。
懒汉式:吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式。
懒汉方式最核心的思想是 "延时加载"。(例如我们之前所学过的写时拷贝)从而能够优化服务器的启动速度。
1.饿汉模式
类在被加载的时候会实例化一个对象
template <typename T> class Singleton { private: static Singleton<T> data;//饿汉模式,在加载的时候对象就已经存在了 public: static Singleton<T>* GetInstance() { return &data; } };
2.懒汉模式
template <typename T> class Singleton { private: static Singleton<T>* inst; //懒汉式单例,只有在调用GetInstance时才会实例化一个单例对象 public: static Singleton<T>* GetInstance() { if (inst == NULL) { inst = new Singleton<T>(); } return inst; } };
3.单例模式线程池(懒汉模式)
threadpool.hpp
#include <iostream> #include <pthread.h> #include <ctime> #include <cstdlib> #include <unistd.h> #include <queue> using namespace std; namespace qzh { const int max = 5; template <class T> class Pthreadpool { private: int _cap; queue<T> _taskque; pthread_mutex_t _mtx; pthread_cond_t _cod; static Pthreadpool<T> *ins; private: Pthreadpool(int cap = max) : _cap(cap) { pthread_mutex_init(&_mtx, nullptr); pthread_cond_init(&_cod, nullptr); } Pthreadpool(const Pthreadpool<T> &tp) = delete; Pthreadpool<T> &operator=(Pthreadpool<T> &tp) = delete; public: static Pthreadpool<T> *GetInstance() { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; if (ins == nullptr) { pthread_mutex_lock(&lock); if (ins == nullptr) { ins = new Pthreadpool<T>(); cout << "首次加载对象" << endl; ins->InitPool(); } pthread_mutex_unlock(&lock); } return ins; } void Lock() { pthread_mutex_lock(&_mtx); } void Unlock() { pthread_mutex_unlock(&_mtx); } bool Isempty() { return _taskque.empty(); } void Wait() { pthread_cond_wait(&_cod, &_mtx); } void Wakeup() { pthread_cond_signal(&_cod); } //线程不可执行类内成员方法,因为这个方法具有隐含this指针, //可以访问静态方法,static方法没有this static void *Rountine(void *args) { pthread_detach(pthread_self()); Pthreadpool<T> *tp = (Pthreadpool<T> *)args; while (1) { tp->Lock(); while (tp->Isempty()) { tp->Wait(); } T t; tp->PopTask(&t); tp->Unlock(); t(); } } void InitPool() { pthread_t tid; for (int i = 0; i < _cap; i++) { pthread_create(&tid, nullptr, Rountine, (void *)this); } } void PopTask(T *out) { *out = _taskque.front(); _taskque.pop(); } void PushTask(const T &in) { Lock(); _taskque.push(in); Unlock(); Wakeup(); } ~Pthreadpool() { pthread_mutex_destroy(&_mtx); pthread_cond_destroy(&_cod); } }; template <class T> Pthreadpool<T> *Pthreadpool<T>::ins = nullptr; }
main.cc
#include"pthreadpool.hpp" #include"task.hpp" using namespace qzh1; using namespace qzh; int main() { srand((long long)time(nullptr)); cout<<"当前正在运行其他代码"<<endl; cout<<"当前正在运行其他代码"<<endl; cout<<"当前正在运行其他代码"<<endl; cout<<"当前正在运行其他代码"<<endl; cout<<"当前正在运行其他代码"<<endl; sleep(3); while(1) { usleep(100); int x=rand()%20+1; int y=rand()%20+1; string ops="+-*/%"; Task t(x,y,ops[rand()%5]); Pthreadpool<Task>::GetInstance()->PushTask(t); } return 0; }
task.hpp
#include<iostream> using namespace std; namespace qzh1 { class Task { private: int _x; int _y; char _op; public: Task(int x,int y,char op):_x(x),_y(y),_op(op) {} Task() {} ~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; } cout<<"当前任务正在被thread["<<pthread_self()<<"]处理,结果为: "<<_x<<_op<<_y<<"="<<res<<endl; } int operator()() { return Run(); } }; }