线程池
概念
概念:创建一堆线程,循环等待处理任务,是非常典型的消费者与生产者模型; 原理:一堆线程 + 线程安全的任务队列,其他线程将任务抛入线程安全的任务队列中,线程池中的线程从任务队列中获取任务进行处理;
优点
线程池优点:从使用线程池和不适用线程池两方面来说
不使用线程池:若要处理大量请求,使用单执行流程效率较低,因此采用多执行流程(多线程)来提高处理效率——产生一个请求,则创建一个线程,然后去处理请求,最后进行线程销毁; 若处理一个任务的总时间中,创建线程与销毁线程的时间占据大量时间比例,那么则意味着 CPU 资源大部分时间都消耗在线程的创建与销毁而不是处理任务,这样会影响缓存局部性和整体性能; 使用线程池:线程池中的线程在创建之后不销毁,而是循环从队列中取出任务进行处理,避免了频繁进行线程的创建与销毁带来的时间成本与代价; 线程池不仅能够保证内核的充分利用,还能防止过分调度,因为线程池中创建的线程以及缓冲区大小有最大数量的限制,所以如果在一瞬间有大量任务涌入,就可以避免峰值压力带来的风险;
应用
需要大量的线程来完成任务,且完成任务的时间比较短:WEB 服务器完成网页请求这样的任务,使用线程池技术是非常合适的,因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数,但对于长时间的任务,比如一个 Telnet 连接请求,线程池的优点就不明显了,因为 Telnet 会话时间比线程的创建时间大多了; 对性能要求苛刻的应用:比如要求服务器迅速响应客户请求; 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用:突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,但是短时间内产生大量线程可能使内存到达极限,出现错误;
实现
实现:线程池的作用就是针对大量任务进行处理,但是任务类型多种多样,而线程入口函数却是固定的,如何实现工作线程针对不同的任务进行不同的处理呢?
在线程入口函数中,分辨任务类型,调用不同处理接口,不过该方法不太好实现,需要程序员来手动确定传入的任务如何处理,如果一百个任务有一百种处理方法,那么对程序员是不友好的; 其他线程在通过任务队列传入任务的同时,也把这个任务的处理方法传进来,线程池中的线程只需要使用处理方法来处理任务即可,不用关注什么样的任务该怎样去处理,当一个没有感情的处理任务机器,该方法十分推荐;
#include <iostream>
#include <queue>
#include <pthread.h>
#define MAX_QUEUE 10
#define MAX_THREAD 5
typedef void ( * handler_t) ( int data) ;
class ThreadTask {
private :
int _data;
handler_t _handler;
public :
ThreadTask ( ) { }
ThreadTask ( int data, handler_t handler)
: _data ( data)
, _handler ( handler)
{ }
void SetTask ( int data, handler_t handler) {
_data = data;
_handler = handler;
}
void Run ( ) {
return _handler ( _data) ;
}
} ;
class BlockQueue {
private :
int _capacity;
std:: queue< ThreadTask> _queue;
pthread_mutex_t _mutex;
pthread_cond_t _cond_pro;
pthread_cond_t _cond_cus;
public :
BlockQueue ( int cap = MAX_QUEUE)
: _capacity ( cap)
{
pthread_mutex_init ( & _mutex, NULL ) ;
pthread_cond_init ( & _cond_pro, NULL ) ;
pthread_cond_init ( & _cond_cus, NULL ) ;
}
~ BlockQueue ( ) {
pthread_mutex_destroy ( & _mutex) ;
pthread_cond_destroy ( & _cond_pro) ;
pthread_cond_destroy ( & _cond_cus) ;
}
bool Push ( ThreadTask & data) {
pthread_mutex_lock ( & _mutex) ;
while ( _queue. size ( ) == _capacity) {
pthread_cond_wait ( & _cond_pro, & _mutex) ;
}
_queue. push ( data) ;
pthread_cond_signal ( & _cond_cus) ;
pthread_mutex_unlock ( & _mutex) ;
return true ;
}
bool Pop ( ThreadTask * data) {
pthread_mutex_lock ( & _mutex) ;
while ( _queue. empty ( ) ) {
pthread_cond_wait ( & _cond_cus, & _mutex) ;
}
* data = _queue. front ( ) ;
_queue. pop ( ) ;
pthread_cond_signal ( & _cond_pro) ;
pthread_mutex_unlock ( & _mutex) ;
return true ;
}
} ;
class ThreadPool {
public :
ThreadPool ( int tnum = MAX_THREAD, int qnum = MAX_QUEUE)
: _thread_num ( tnum)
, _queue ( qnum)
{ }
bool Init ( ) {
int ret;
pthread_t tid;
for ( int i = 0 ; i < _thread_num; i++ ) {
* * * * * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * * *
ret = pthread_create ( & tid, NULL , thr_entry, ( void * ) this ) ;
if ( ret != 0 ) {
std:: cout << "thread create error\n" ;
return false ;
}
pthread_detach ( tid) ;
}
return true ;
}
bool Push ( ThreadTask & task) {
_queue. Push ( task) ;
return true ;
}
private :
static void * thr_entry ( void * arg) {
ThreadPool* this = ( ThreadPool* ) arg;
while ( 1 ) {
ThreadTask task;
this - > _queue. Pop ( & task) ;
task. Run ( ) ;
}
return NULL ;
}
private :
int _thread_num;
BlockQueue _queue;
} ;
int main ( int argc, char * argv[ ] ) {
return 0 ;
}
单例模式
概念
单例模式:是一种非常典型的设计模式,该模式下,一份资源只能被加载一次、一个类只能实例化一个对象,并且向外提供统一的接口来供使用;
实现
饿汉方式:资源在程序初始化阶段就完成加载——以空间换时间;
静态修饰资源:保证资源只有一份,并且静态成员在程序初始化阶段就完成了初始化加载; 构造函数私有化:在外部无法进行对象的构造,因此可以保证类的对象只有一个,我们可以在类内部构造一个对象以供使用;
template < class T >
class singleton {
private :
static T _data;
singleton ( ) { }
public :
T* get_data ( ) {
return & _data;
}
} ;
懒汉方式:资源在使用的时候再去加载——延迟加载;
静态修饰资源指针:静态可以保证资源指针只被加载一份,指针可以在使用时再加载资源,也就是延迟加载; volatile
修饰资源指针:防止编译器过度优化,毕竟资源在使用时才会加载到指针,所以如果编译器过度优化,那么就不能拿到资源了;构造函数私有化:在外部无法进行对象的构造,因此可以保证类的对象只有一个,我们可以在类内部构造一个对象以供使用; 加锁保护:因为是使用时才进行加载资源,所以在多线程运行下,很可能多个线程对资源进行访问,发现此时没有资源,然后都向其加载资源,那么会造成资源重复加载问题,设置互斥锁就可避免这种情况,保护线程安全; 二次检测:当多个线程去访问资源时,如果资源不存在,我们再进行加锁,然后加载资源,这样子就可以避免当资源存在时也有线程去加锁的问题,可以大大提高效率;
template < class T >
class singleton {
private :
volatile static T _data;
singleton ( ) { }
static std:: mutex _mutex;
public :
volatile T* get_data ( ) {
if ( _data == nullptr ) {
_mutex. lock ( ) ;
if ( _data == numllptr) {
_data = new T ( ) ;
}
_mutex. unlock ( ) ;
}
return & _data;
}
} ;