重庆市建设工程造价站,校园网站建设途径,网站开发好找工作吗,小程序商城哪家好经销商总言 多线程#xff1a;生产者消费者模型与两种实现方式#xff08;条件变量、信号量#xff09;、线程池。  文章目录 总言4、生产者消费者模型4.1、基本概念4.2、基于BlockingQueue的生产者消费者模型#xff08;理解条件变量#xff09;4.2.1、单生产者单消费者模式生产者消费者模型与两种实现方式条件变量、信号量、线程池。  文章目录 总言4、生产者消费者模型4.1、基本概念4.2、基于BlockingQueue的生产者消费者模型理解条件变量4.2.1、单生产者单消费者模式1.04.2.1.1、阻塞队列 BlocQueue.hpp充当交易场所4.2.2.2、生产者消费者线程 ConProd.cc4.2.2.3、演示结果与补充说明 4.2.2、多生产者多消费者模式2.04.2.2.1、引入任务派发 Task.hpp4.2.2.2、加锁方式设计 lockGuard.hppPAII风格4.2.2.3、整体4.2.2.4、演示结果与补充说明  4.3、基于环形队列的生产消费模型4.3.1、POSIX信号量介绍4.3.2、结构需求和结构说明4.3.3、单生产者单消费者模式1.04.3.3.1、环形队列 ringQueue.hpp充当交易场所4.3.3.2、信号量对象 Sem.hpp4.3.3.3、生产者消费者线程 testMain.cc4.3.3.4、演示结果 4.3.4、多生产者多消费者模式2.04.3.4.1、相关说明4.3.4.2、整体演示   5、线程池5.1、概念介绍5.2、基本演示5.2.1、thread.hpp5.2.2、threadpool.hpp5.2.3、testMain.cc5.2.4、Task.hpp、log.hpp、lockGuard.hpp   4、生产者消费者模型 
4.1、基本概念 说明 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯而通过阻塞队列来进行通讯所以生产者生产完数据之后不用等待消费者处理直接扔给阻塞队列消费者不找生产者要数据而是直接从阻塞队列里取阻塞队列就相当于一个缓冲区平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。 PS在下述编码实现过程中始终要站在三种关系、两种角色、一个场所这类角色属性的角度来分析考虑有助于理解代码操作。          
4.2、基于BlockingQueue的生产者消费者模型理解条件变量 阻塞队列与普通的队列区别在于       当队列为空时从队列获取元素的操作将会被阻塞直到队列中被放入了元素       当队列为满时往队列里存放元素的操作也会被阻塞直到有元素被从队列中取出。       PS以上空和满的操作是基于不同的线程来说的。  4.2.1、单生产者单消费者模式1.0 
4.2.1.1、阻塞队列 BlocQueue.hpp充当交易场所 框架搭建如下图          具体实现如下 
#pragma once
#includeiostream
#includepthread.h
#includetime.h
#includeunistd.h
#includequeueusing namespace std;int gDefaultCap  5;//默认的阻塞队列容量上限template class T // 模板类型
class BlockQueue
{
private:bool isQueueFull(){return _bq.size()  _capacity;}bool isQueueEmpty(){return _bq.size()  0;}public://构造函数初始化容量上限、初始化互斥锁、初始化条件变量BlockQueue(int capacity  gDefaultCap): _capacity(capacity)//容量上限由用户决定需要多大容量{pthread_mutex_init(_mutex, nullptr);pthread_cond_init(_Empty, nullptr);pthread_cond_init(_Full, nullptr);}//析构函数销毁互斥锁、销毁条件变量~BlockQueue(){pthread_mutex_destroy(_mutex);pthread_cond_destroy(_Empty);pthread_cond_destroy(_Full);}//Push:放数据的接口由生产者线程不断向阻塞队列中放数据void Push(const T in)   {//临界资源区先上锁pthread_mutex_lock(_mutex);//判断当前临界资源是否满足条件对生产者、isQueueFullwhile(isQueueFull()) pthread_cond_wait(_Full, _mutex);//代码执行到此步当前生产者线程持有锁且阻塞队列未满---可放入数据_bq.push(in);//完成后解锁并唤醒消费者线程告诉它此时交易场所中有资源可以取用pthread_mutex_unlock(_mutex);pthread_cond_signal(_Empty);}//Pop:取数据的接口由消费者线程不断从阻塞队列中取数据void Pop(T* out){//临界资源区先上锁pthread_mutex_lock(_mutex);//判断当前临界资源是否满足条件对消费者、isQueueEmptywhile(isQueueEmpty()) pthread_cond_wait(_Empty, _mutex);//代码执行到此步当前消费者线程持有锁且阻塞队列不空---可取到数据*out  _bq.front();_bq.pop();//完成后解锁并唤醒生产者线程告诉它此时交易场所中资源被取出可补充pthread_mutex_unlock(_mutex);pthread_cond_signal(_Full);}private:queueT _bq;           // 使用库里的队列做阻塞队列int _capacity;          // 阻塞队列的容量上限pthread_mutex_t _mutex; // 互斥锁保证队列安全pthread_cond_t _Empty;  // 判断阻塞队列为空的条件变量关心该条件的是消费者空了就不能取数据pthread_cond_t _Full;   // 判断阻塞队列为满的条件变量关心该条件的是生产者满了就不能放数据// PS一些解释/*生产者消费者模型中阻塞队列充当了交易场所的角色生产者消费者线程能同时访问该交易场所即访问了临界资源。1、多线程同时Push/pop因此需要互斥保证临界资源一次只能让一线程操作2、在Push的同时存在Pop因此需要同步保证访问临界资源的顺序性。*/
}; 4.2.2.2、生产者消费者线程 ConProd.cc 
#include BlockQueue.hpp// 生产者线程调用push接口存入数据
void *Produce(void *args)
{BlockQueueint *bqueue  (BlockQueueint *)args;// 生产数据while (true){int data  rand() % 100;cout  producer, 生产一个数据:  data  endl;bqueue-Push(data);sleep(1);}return nullptr;
}// 消费者线程调用pop接口取出数据
void *Consume(void *args)
{BlockQueueint *bqueue  (BlockQueueint *)args;// 消费数据while (true){int data;bqueue-Pop(data);cout  consumer, 消费一个数据:   data  endl;sleep(1);}return nullptr;
}int main()
{//用于充当数据资源srand((unsigned int)time(nullptr));//创建交易场所阻塞队列BlockQueueint* bqueue  new BlockQueueint();//创建两类线程生产者、消费者pthread_t ptor, cmer;pthread_create(ptor, nullptr, Produce, (void*)bqueue);//arg参数传入阻塞队列让两个线程能够看到同一个交易场所pthread_create(cmer, nullptr, Consume, (void*)bqueue);//线程终止pthread_join(ptor, nullptr);pthread_join(ptor, nullptr);delete bqueue;//注意释放空间return 0;
}4.2.2.3、演示结果与补充说明 演示结果    1、可根据需求适当加入策略。如通过sleep控制生产消费双方线程速度①生产慢、消费快②生产快、消费慢③生产消费速度同。等。   2、但无论上述哪一情况阻塞队列实际不关心生产者消费者线程谁先运行或者谁在等待因为在内部实现中已经考虑到执行流程。            对于生产者消费者模型其效率优势体现在哪   以上述单生产者单消费者为例一定程度上缓解了生产者和消费者之间的数据处理能力。    对于多生产者多消费者除了上述该点外也有其它意义后续说明。                
4.2.2、多生产者多消费者模式2.0 
4.2.2.1、引入任务派发 Task.hpp 
#pragma once
#include iostream
#include functionaltypedef std::functionint(int, int) func_t;// 用于派发任务对象可根据需求设置此处为演示两数计算
class Task
{
public:Task(){}; // 默认无参构造Task(int x, int y, func_t func) // 需要我们自己传递函数和对应变量: _x(x), _y(y), _func(func){}int operator()(){return _func(_x, _y);}public:int _x;int _y;func_t _func; // 函数指针
};4.2.2.2、加锁方式设计 lockGuard.hppPAII风格 
#pragma once
#include iostream
#includepthread.hclass Mutex
{
public:Mutex(pthread_mutex_t *pmutex): _pmutex(pmutex){}void lock()//加锁{pthread_mutex_lock(_pmutex);}void unlock()//解锁{pthread_mutex_unlock(_pmutex);}private:pthread_mutex_t *_pmutex;
};class lockGuard
{
public:lockGuard(pthread_mutex_t* pmutex)//构造在构造时加锁:_mutex(pmutex)//初始化列表初始化_mutex时调用Mutex的构造函数需要传入pthread_mutex_t * 类型变量{_mutex.lock();//上锁}~lockGuard()//析构在对类析构时顺带就解锁{_mutex.unlock();//解锁}
private:Mutex _mutex;
};4.2.2.3、整体 实则改动不大。   1、对BlockQueue.hpp修改加锁风格其余部分大体不变。 
#pragma once
#includeiostream
#includepthread.h
#includetime.h
#includeunistd.h
#includequeue
#includelockGuard.hppusing namespace std;int gDefaultCap  5;//默认的阻塞队列容量上限template class T // 模板类型
class BlockQueue
{
private:bool isQueueFull(){return _bq.size()  _capacity;}bool isQueueEmpty(){return _bq.size()  0;}public://构造函数初始化容量上限、初始化互斥锁、初始化条件变量BlockQueue(int capacity  gDefaultCap): _capacity(capacity)//容量上限由用户决定需要多大容量{pthread_mutex_init(_mutex, nullptr);pthread_cond_init(_Empty, nullptr);pthread_cond_init(_Full, nullptr);}//析构函数销毁互斥锁、销毁条件变量~BlockQueue(){pthread_mutex_destroy(_mutex);pthread_cond_destroy(_Empty);pthread_cond_destroy(_Full);}//Push:放数据的接口由生产者线程不断向阻塞队列中放数据void Push(const T in)   {//临界资源区先上锁lockGuard lockguard(_mutex);//创建该对象那么在构造时会加锁在析构时会解锁。局部变量声明周期在该函数中//判断当前临界资源是否满足条件对生产者、isQueueFullwhile(isQueueFull()) pthread_cond_wait(_Full, _mutex);//代码执行到此步当前生产者线程持有锁且阻塞队列未满---可放入数据_bq.push(in);//完成后唤醒消费者线程告诉它此时交易场所中有资源可以取用pthread_cond_signal(_Empty);//出了作用范围lockguard析构时解锁}//Pop:取数据的接口由消费者线程不断从阻塞队列中取数据void Pop(T* out){//临界资源区先上锁lockGuard lockguard(_mutex);//创建该对象那么在构造时会加锁在析构时会解锁。局部变量声明周期在该函数中//判断当前临界资源是否满足条件对消费者、isQueueEmptywhile(isQueueEmpty()) pthread_cond_wait(_Empty, _mutex);//代码执行到此步当前消费者线程持有锁且阻塞队列不空---可取到数据*out  _bq.front();_bq.pop();//完成后,唤醒生产者线程告诉它此时交易场所中资源被取出可补充pthread_cond_signal(_Full);//出了作用范围lockguard析构时解锁}private:queueT _bq;           // 使用库里的队列做阻塞队列int _capacity;          // 阻塞队列的容量上限pthread_mutex_t _mutex; // 互斥锁保证队列安全pthread_cond_t _Empty;  // 判断阻塞队列为空的条件变量关心该条件的是消费者空了就不能取数据pthread_cond_t _Full;   // 判断阻塞队列为满的条件变量关心该条件的是生产者满了就不能放数据// PS一些解释/*生产者消费者模型中阻塞队列充当了交易场所的角色生产者消费者线程能同时访问该交易场所即访问了临界资源。1、多线程同时Push/pop因此需要互斥保证临界资源一次只能让一线程操作2、在Push的同时存在Pop因此需要同步保证访问临界资源的顺序性。*/
}; 2、对ConProd.cc创建多线程多个生产者、多个消费者此部分内容在之前的环节中也有涉及派发任务与接收任务任务对象。 
#include BlockQueue.hpp
#include Task.hppint add(int x, int y)
{return xy;
}// 生产者线程调用push接口存入数据
void *Produce(void *args)
{BlockQueueTask *bqueue  (BlockQueueTask *)args;// 派发任务while (true){//生产前环节:获取一个任务int x  rand() % 100;usleep(1000);//主要是随机数由时间戳生成这里延长些间隔时间int y  rand() % 100;Task assign(x, y, add);//生产环节生产者生产任务到阻塞队列printf(%p---productor: (%d , %d )\n,pthread_self(), assign._x, assign._y);bqueue-Push(assign);sleep(2);}return nullptr;
}// 消费者线程调用pop接口取出数据
void *Consume(void *args)
{BlockQueueTask *bqueue  (BlockQueueTask *)args;while (true){//消费环节:消费者从阻塞队列中获取任务Task assign;bqueue-Pop(assign);//消费后环节处理任务printf(%p---consumer: addtion result is  %d  %d  %d\n, pthread_self(), assign._x, assign._y, assign());printf(\n);sleep(2);}return nullptr;
}int main()
{//用于充当任务对象派发的数据资源srand((unsigned int)time(nullptr));//创建交易场所阻塞队列BlockQueueTask* bqueue  new BlockQueueTask();//向阻塞队列中存入的数据是Task任务对象//创建两类线程生产者、消费者pthread_t ptor[2], cmer[2];pthread_create(ptor, nullptr, Produce, (void*)bqueue);//arg参数传入阻塞队列让两个线程能够看到同一个交易场所pthread_create(ptor1, nullptr, Produce, (void*)bqueue);//arg参数传入阻塞队列让两个线程能够看到同一个交易场所pthread_create(cmer, nullptr, Consume, (void*)bqueue);pthread_create(cmer1, nullptr, Consume, (void*)bqueue);//线程终止pthread_join(ptor[0], nullptr);pthread_join(ptor[1], nullptr);pthread_join(cmer[0], nullptr);pthread_join(cmer[1], nullptr);delete bqueue;//注意释放空间return 0;
}4.2.2.4、演示结果与补充说明 演示结果  对多生产者多消费者意义的补充说明    至于使用单生产单消费还是多生产多消费取决于具体场景中接收任务和处理任务的耗时情况。假设这两环节在整体流程体系中所占据时间比例很小即任务简单都不太需要耗费时间那么此时使用多生产多消费反而是一种累赘行为。             
4.3、基于环形队列的生产消费模型 
4.3.1、POSIX信号量介绍 1、信号量介绍   1、什么是信号量   POSIX信号量和SystemV信号量作用相同都是用于同步操作达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。 信号量本质是一个计数器用于预定资源。访问临界资源的时候必须先申请信号量资源(sem–预订资源P)使用完毕信号量资源(sem释放资源V)         2、如何理解信号量的使用   我们申请了一个信号量当前执行流一定具有一个资源可以被使用。至于是哪一个资源需要结合场景自定义编码完成。         2、相关接口   PS要理解信号量接口在生产者消费者模型中的使用建议将相关接口的基本描述信息DESCRIPTION大致看一遍man。理解下述几个接口的含义。      初始化信号量 
NAMEsem_init - initialize an unnamed semaphoreSYNOPSIS#include semaphore.hint sem_init(sem_t *sem, int pshared, unsigned int value);参数pshared0表示线程间共享非零表示进程间共享value信号量初始值DESCRIPTIONsem_init()  initializes  the  unnamed  semaphore at the address pointed to by sem.The value argument specifies the initial value for the semaphore. 销毁信号量 
NAMEsem_destroy - destroy an unnamed semaphoreSYNOPSISint sem_destroy(sem_t *sem);等待信号量 
NAMEsem_wait, sem_timedwait, sem_trywait - lock a semaphoreSYNOPSIS#include semaphore.hint sem_wait(sem_t *sem);//P()等待信号量会将信号量的值减1int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);DESCRIPTION //具体理解信号量的值value递减至0时会做什么sem_wait() decrements (locks) the semaphore pointed to by sem.  If the semaphoresvalue is greater than zero, then the decrement proceeds, and the function returns,immediately.   If the semaphore currently has the value zero, then the call blocksuntil either it becomes possible to perform the  decrement  (i.e.,  the  semaphorevalue rises above zero), or a signal handler interrupts the call. 发布信号量 
NAMEsem_post - unlock a semaphoreSYNOPSIS#include semaphore.hint sem_post(sem_t *sem);//V()功能发布信号量表示资源使用完毕可以归还资源了。将信号量值加1。DESCRIPTION //具体理解信号量增加时是在做什么sem_post()  increments  (unlocks)  the  semaphore pointed to by sem.  If the sema-phores value consequently becomes greater than  zero,  then  another  process  orthread blocked in a sem_wait(3) call will be woken up and proceed to lock the sem-aphore.PV操作是一种实现进程互斥与同步的有效方法与信号量的处理相关。P表示通过的意思V表示释放的意思。   PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来当信号量的值为0时表示期望的消息尚未产生当信号量的值非0时表示期望的消息已经存在。用PV操作实现进程同步时调用P操作测试消息是否到达调用V操作发送消息。          
4.3.2、结构需求和结构说明 4.3.3、单生产者单消费者模式1.0 
4.3.3.1、环形队列 ringQueue.hpp充当交易场所 
#pragma once
#includeiostream
#includepthread.h
#includevector
#includeunistd.h
#includetime.h
#includeSem.hpp
using namespace std;int g_defualt_num  5;//环形队列默认容量上限templateclass T
class ringQueue
{ 
public://构造初始化数据ringQueue(int num  g_defualt_num):_ring_queue(num)//这里是对vector对象进行构造explicit vector (size_type n, const value_type val  value_type());,_num(num),p_step(0)//初始下标为0,c_step(0)//初始下标为0,space_sem(num)//构造信号量初始时空间资源有num个,data_sem(0)//构造信号量初始时数据资源有0个{ }//析构~ringQueue(){}// 向环形队列中放入数据生产者void push(const T in){// 申请信号量预定空间资源space_sem.P();// 在特定位置生产数据_ring_queue[p_step]  in;p_step % _num; //[0,_num-1]// 生产成功则意味着数据资源多了一个data_sem.V();}// 向环形队列中取出数据消费者void pop(T *out){// 申请信号量预定数据资源data_sem.P();// 在特定位置消费数据*out  _ring_queue[c_step];c_step % _num;// 消费成功则意味着空间资源多了一个space_sem.V();}private:vectorT _ring_queue;//环形队列以数组的方式实现int _num;//环形队列容量上限int p_step;//生产者下标在当前下标位置放入资源int c_step;//消费者下标在当前下标位置取出资源Sem space_sem;//生产者信号量空间资源Sem data_sem;//消费者信号量数据资源
};4.3.3.2、信号量对象 Sem.hpp 
#ifndef _SEM_HPP_
#define _SEM_HPP_#includeiostream
#includesemaphore.h//信号量对象因环形队列中需要用到不止一个信号量包括其相关操作故对其进行封装处理
class Sem
{
public://构造:对信号量进行初始化设置该信号量的初始值。Sem(unsigned int value){sem_init(_sem, 0, value);}//析构销毁信号量~Sem(){sem_destroy(_sem);}//P操作资源预定信号量--void P(){sem_wait(_sem);}//V操作资源释放信号量void V(){sem_post(_sem);}private:sem_t _sem;
};#endif4.3.3.3、生产者消费者线程 testMain.cc 
#includeringQueue.hpp
void* Produce(void* args)
{ringQueueint* rq  (ringQueueint*)args;while (true){// 创建数据int data  rand() % 100 1 ;// 生产者将数据存入交易场所中环形队列rq-push(data);printf(生产者: %d\n,data);sleep(1);}
}void* Consume(void* args)
{ringQueueint* rq  (ringQueueint*)args;while(true){// 消费者从交易场所取出数据并做后续处理int data;rq-pop(data);printf(消费者%d\n, data);sleep(1);}
}int main()
{//用随机数模拟数据srand((unsigned int)time(nullptr));//创建交易场所环形队列ringQueueint* rq  new ringQueueint();//创建并初始化生产者、消费者线程pthread_t ptor, cmer;pthread_create(ptor, nullptr, Produce, (void*)rq);pthread_create(cmer, nullptr, Consume, (void*)rq);//线程捕获pthread_join(ptor,nullptr);pthread_join(cmer,nullptr);delete rq;return 0;
}4.3.3.4、演示结果 演示结果对于数据输出部分可以根据需求调整修改。 4.3.4、多生产者多消费者模式2.0 
4.3.4.1、相关说明 4.3.4.2、整体演示 对testMain.cc多增了线程创建和线程终止。 
#includeringQueue.hpp
void* Produce(void* args)
{ringQueueint* rq  (ringQueueint*)args;while (true){// 创建数据int data  rand() % 100 1 ;// 生产者将数据存入交易场所中环形队列rq-push(data);printf(生产者--%p: %d\n,pthread_self(),data);sleep(1);}
}void* Consume(void* args)
{ringQueueint* rq  (ringQueueint*)args;while(true){// 消费者从交易场所取出数据并做后续处理int data;rq-pop(data);printf(消费者--%p: %d\n, pthread_self(),data);sleep(1);}
}int main()
{//用随机数模拟数据srand((unsigned int)time(nullptr)^getpid());//创建交易场所环形队列ringQueueint* rq  new ringQueueint();//创建并初始化生产者、消费者线程pthread_t ptor[2], cmer[2];pthread_create(ptor, nullptr, Produce, (void*)rq);pthread_create(ptor1, nullptr, Produce, (void*)rq);pthread_create(cmer, nullptr, Consume, (void*)rq);pthread_create(cmer1, nullptr, Consume, (void*)rq);//线程捕获pthread_join(ptor[0],nullptr);pthread_join(ptor[1],nullptr);pthread_join(cmer[0],nullptr);pthread_join(cmer[1],nullptr);delete rq;return 0;
}对ringQueue.hpp在环形队列中分别为生产者消费者创建了各自的锁使用于push、pop接口。 
#pragma once
#includeiostream
#includepthread.h
#includevector
#includeunistd.h
#includetime.h
#includeSem.hpp
using namespace std;int g_defualt_num  5;//环形队列默认容量上限templateclass T
class ringQueue
{ 
public://构造初始化数据ringQueue(int num  g_defualt_num):_ring_queue(num)//这里是对vector对象进行构造explicit vector (size_type n, const value_type val  value_type());,_num(num),p_step(0)//初始下标为0,c_step(0)//初始下标为0,space_sem(num)//构造信号量初始时空间资源有num个,data_sem(0)//构造信号量初始时数据资源有0个{ pthread_mutex_init(p_mutex, nullptr);pthread_mutex_init(c_mutex, nullptr);}//析构~ringQueue(){pthread_mutex_destroy(p_mutex);pthread_mutex_destroy(c_mutex);}// 向环形队列中放入数据生产者void push(const T in){// 申请信号量预定空间资源space_sem.P();//访问临界资源前加锁生产者内部竞争胜者持锁访问交易场所pthread_mutex_lock(p_mutex);// 在特定位置生产数据_ring_queue[p_step]  in;p_step % _num; //[0,_num-1]//解锁pthread_mutex_unlock(p_mutex);// 生产成功则意味着数据资源多了一个data_sem.V();}// 向环形队列中取出数据消费者void pop(T *out){// 申请信号量预定数据资源data_sem.P();//访问临界资源前加锁消费者内部竞争胜者持锁访问交易场所pthread_mutex_lock(c_mutex);// 在特定位置消费数据*out  _ring_queue[c_step];c_step % _num;//解锁pthread_mutex_unlock(c_mutex);// 消费成功则意味着空间资源多了一个space_sem.V();}private:vectorT _ring_queue;//环形队列以数组的方式实现int _num;//环形队列容量上限int p_step;//生产者下标在当前下标位置放入资源int c_step;//消费者下标在当前下标位置取出资源Sem space_sem;//生产者信号量空间资源Sem data_sem;//消费者信号量数据资源pthread_mutex_t p_mutex;//生产者内部使用的锁pthread_mutex_t c_mutex;//消费者内部使用的锁};演示结果还可以将其修改为派发任务的情况。 5、线程池 
5.1、概念介绍 线程池:   一种线程使用模式。线程过多会带来调度开销进而影响缓存局部性和整体性能而线程池维护着多个线程等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价线程池不仅能够保证内核的充分利用还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。       线程池的应用场景   1、需要大量的线程来完成任务且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务使用线程池技术是非常合适的。因为单个任务小而任务数量巨大你可以想象一个热门网站的点击次数。 但对于长时间的任务比如一个Telnet连接请求线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。   2、对性能要求苛刻的应用比如要求服务器迅速响应客户请求。   3、接受突发性的大量请求但不至于使服务器因此产生大量线程的应用。突发性大量客户请求在没有线程池情况下将产生大量线程虽然理论上大部分操作系统线程数目最大值不是问题短时间内产生大量线程可能使内存到达极限出现错误。          
5.2、基本演示 PS以下只是最基本的演示案例可根据需求继续修改、完善、补充。 
5.2.1、thread.hpp 
#pragma once
#includeiostream
#includestring
#includepthread.husing namespace std;
typedef void* (*func_t)(void*);//函数指针此处用于线程表示线程的执行函数//args传参设置设置成类增加args传参选择
class ThreadData
{
public:string _name;//对应线程名称void* _args;//对应线程回调函数中args参数
};class Thread
{
public:Thread(int inode, func_t rountine, void* args):_routine_func(rountine)//注意这里线程的执行函数、参数args都是需要通过外部传入的{char buffer[64];snprintf(buffer, sizeof(buffer), thread-%d,inode);_name  buffer;_tdata._args  args;_tdata._name  _name;}~Thread(){}void start()//启动线程用于创建线程构造函数只是做了线程名称、ID等各参数设置实则并未真正创建出线程{pthread_create(_tid, nullptr, _routine_func, (void*)_tdata);}void join()//终止线程{pthread_join(_tid, nullptr);}private:string _name;//线程名pthread_t _tid;//线程IDfunc_t _routine_func;//线程的执行函数ThreadData _tdata;
};5.2.2、threadpool.hpp 
#pragma once#includevector
#includequeue
#include thread.hpp
#include lockGuard.hpp//线程池内部放置有多个处于等待状态的线程消费者当有任务派遣进入时线程池内置的仓库存放任务的队列可唤醒线程处理任务
//相对于把任务到来再从零开始创建线程此法中线程属于预备状态那么有任务时直接派发处理即可。int g_thread_num  5;templateclass T//模板用于表示交易场所中资源数据类型如任务对象
class threadPool
{
private://此处接口主要是用于rountine线程执行函数其为静态成员函数无法直接访问到类中非静态的成员变量bool is_TaskQueue_Empty()//用于判断任务队列是否为空{return _task_queue.empty();}void condwait()//根据条件变量挂起对应线性{pthread_cond_wait(_cond,_mutex);}pthread_mutex_t* getMutex()//用于获取锁{return _mutex;}T getTask(){T tmp  _task_queue.front();_task_queue.pop();return tmp;}public:threadPool(int thread_num  g_thread_num)//构造函数:_num(thread_num)//待创建线程的个数{//初始化锁、条件变量pthread_mutex_init(_mutex, nullptr);pthread_cond_init(_cond, nullptr);//初始化线程对象for(int i  i; i  _num; i){_threads.push_back(new Thread(i, routine, this));}}~threadPool(){//销毁锁、条件变量pthread_mutex_destroy(_mutex);pthread_cond_destroy(_cond);//线程终止for(auto  itor: _threads){itor-join();delete itor;//销毁构造函数中new出来的空间}}void run()//启动线程池:实际创建线程{for(auto  itor: _threads)//这里遍历调用线程对象中的start用以创建线程{   itor-start();}}//消费者用于执行任务的线程static void* routine(void* args){ThreadData* pdata  (ThreadData*)args;threadPoolT* pthis  (threadPoolT*)pdata-_args;while(true)//线程非执行一次任务就终止{T task;// 此处受制于作用域任务对象需要创建在{}外{                                 // 加上括号的原因是方便在创建lockGuard对象时自动加锁解锁lockGuard lockguard(pthis-getMutex()); // 先上锁while (pthis-is_TaskQueue_Empty())pthis-condwait(); // 判断临界资源是否满足// 到此步骤说明线程持有锁且临界资源满足条件将任务从仓库中取出task  pthis-getTask();}task(pdata-_name);}}//仓库将获取到的任务对象存入队列中void pushTask(const T task){   //仓库属于临界资源放入数据到仓库中是生产者做的事(外部调用)也要加锁处理lockGuard lockguard(_mutex);_task_queue.push(task);//仓库中有数据资源此时可以唤醒被挂起的线程让其执行任务pthread_cond_signal(_cond);}private:vectorThread* _threads;//线程池int _num;//线程池中线程数目queueT _task_queue;//队列:用于充当交易场所pthread_mutex_t _mutex;//交易场所可被多个线程访问属于临界资源故而需要互斥锁pthread_cond_t _cond;//当某一线程持有锁时为了防止其余线程屡次申请锁引入条件变量
};5.2.3、testMain.cc 
#includetime.h
#includeunistd.h
#includethreadPool.hpp
#includeTask.hpp
#includelog.hppint main()
{   //随机数:用于模拟任务数据srand((unsigned long)time(nullptr)^getpid());//创建线程池threadPoolTask* tp  new threadPoolTask();//无参此处线程数目为默认值logMessage(DEBUG,成功创建线程池);//运行线程池tp-run();logMessage(DEBUG,成功运行线程池);//派发任务while(true){int x  rand()%100;usleep(2333);int y  rand()%100;//拉姆达表达式Task t(x,y, [](int x, int y)-int{return x  y; });logMessage(DEBUG,成功制作任务--- add: %d, %d, t._x, t._y);//生产者将任务存放入线性池的仓库中tp-pushTask(t);sleep(1);}return 0;
}5.2.4、Task.hpp、log.hpp、lockGuard.hpp Task.hpp 
#pragma once
#include iostream
#include functional
#include stringtypedef std::functionint(int, int) task_func_t;// 用于派发任务对象可根据需求设置此处为演示两数计算
class Task
{
public:Task(){}; // 默认无参构造Task(int x, int y, task_func_t func) // 需要我们自己传递函数和对应变量: _x(x), _y(y), _func(func){}void operator()(string name)//执行函数调用时报一下线程名称{printf(任务结果: %d  %d  %d \n,_x, _y, _func(_x, _y));}public:int _x;int _y;task_func_t _func; // 函数指针
};log.hpp 
#pragma once#include iostream
#include cstdio
#include cstdarg
#include ctime
#include string// 日志级别
#define DEBUG   0
#define NORMAL  1
#define WARNING 2
#define ERROR   3
#define FATAL   4const char *gLevelMap[]  {DEBUG,NORMAL,WARNING,ERROR,FATAL
};#define LOGFILE ./threadpool.log// 完整的日志功能至少有 日志等级 时间 支持用户自定义(日志内容, 文件行文件名)
void logMessage(int level, const char *format, ...)//const char *format, ... 可变参数
{
#ifndef DEBUG_SHOWif(level DEBUG) return;
#endif//标准部分固定输出的内容char stdBuffer[1024]; time_t timestamp  time(nullptr);snprintf(stdBuffer, sizeof stdBuffer, [%s] [%ld] , gLevelMap[level], timestamp);//自定义部分允许用户根据自己的需求设置char logBuffer[1024]; va_list args; //定义一个va_list对象va_start(args, format); vsnprintf(logBuffer, sizeof logBuffer, format, args);va_end(args); //相当于 args  nullptrprintf(%s%s\n, stdBuffer, logBuffer);
}lockGuard.hpp 
#pragma once
#include iostream
#includepthread.hclass Mutex
{
public:Mutex(pthread_mutex_t *pmutex): _pmutex(pmutex){}void lock()//加锁{pthread_mutex_lock(_pmutex);}void unlock()//解锁{pthread_mutex_unlock(_pmutex);}private:pthread_mutex_t *_pmutex;
};class lockGuard
{
public:lockGuard(pthread_mutex_t* pmutex)//构造在构造时加锁:_mutex(pmutex)//初始化列表初始化_mutex时调用Mutex的构造函数需要传入pthread_mutex_t * 类型变量{_mutex.lock();//上锁}~lockGuard()//析构在对类析构时顺带就解锁{_mutex.unlock();//解锁}
private:Mutex _mutex;
};其它待补充。          文章转载自: http://www.morning.srndk.cn.gov.cn.srndk.cn http://www.morning.kltmt.cn.gov.cn.kltmt.cn http://www.morning.yjxfj.cn.gov.cn.yjxfj.cn http://www.morning.fgxnb.cn.gov.cn.fgxnb.cn http://www.morning.trfh.cn.gov.cn.trfh.cn http://www.morning.bxczt.cn.gov.cn.bxczt.cn http://www.morning.mkrjf.cn.gov.cn.mkrjf.cn http://www.morning.mzrqj.cn.gov.cn.mzrqj.cn http://www.morning.dnmzl.cn.gov.cn.dnmzl.cn http://www.morning.xlztn.cn.gov.cn.xlztn.cn http://www.morning.hpmzs.cn.gov.cn.hpmzs.cn http://www.morning.bsrcr.cn.gov.cn.bsrcr.cn http://www.morning.bnlkc.cn.gov.cn.bnlkc.cn http://www.morning.xcyhy.cn.gov.cn.xcyhy.cn http://www.morning.hxbps.cn.gov.cn.hxbps.cn http://www.morning.qkgwx.cn.gov.cn.qkgwx.cn http://www.morning.kfrhh.cn.gov.cn.kfrhh.cn http://www.morning.ccffs.cn.gov.cn.ccffs.cn http://www.morning.qlznd.cn.gov.cn.qlznd.cn http://www.morning.brnwc.cn.gov.cn.brnwc.cn http://www.morning.nqrdx.cn.gov.cn.nqrdx.cn http://www.morning.glkhx.cn.gov.cn.glkhx.cn http://www.morning.ddtdy.cn.gov.cn.ddtdy.cn http://www.morning.gbfzy.cn.gov.cn.gbfzy.cn http://www.morning.bfgpn.cn.gov.cn.bfgpn.cn http://www.morning.rwtlj.cn.gov.cn.rwtlj.cn http://www.morning.klyzg.cn.gov.cn.klyzg.cn http://www.morning.qlck.cn.gov.cn.qlck.cn http://www.morning.mfqmk.cn.gov.cn.mfqmk.cn http://www.morning.glkhx.cn.gov.cn.glkhx.cn http://www.morning.dhwyl.cn.gov.cn.dhwyl.cn http://www.morning.qwbht.cn.gov.cn.qwbht.cn http://www.morning.tkflb.cn.gov.cn.tkflb.cn http://www.morning.xclgf.cn.gov.cn.xclgf.cn http://www.morning.pfjbn.cn.gov.cn.pfjbn.cn http://www.morning.ryxbz.cn.gov.cn.ryxbz.cn http://www.morning.ckxd.cn.gov.cn.ckxd.cn http://www.morning.gqjqf.cn.gov.cn.gqjqf.cn http://www.morning.rbsmm.cn.gov.cn.rbsmm.cn http://www.morning.qkrqt.cn.gov.cn.qkrqt.cn http://www.morning.ranglue.com.gov.cn.ranglue.com http://www.morning.lngyd.cn.gov.cn.lngyd.cn http://www.morning.jqllx.cn.gov.cn.jqllx.cn http://www.morning.pwggd.cn.gov.cn.pwggd.cn http://www.morning.djpzg.cn.gov.cn.djpzg.cn http://www.morning.wclxm.cn.gov.cn.wclxm.cn http://www.morning.ljdhj.cn.gov.cn.ljdhj.cn http://www.morning.fhkr.cn.gov.cn.fhkr.cn http://www.morning.fqljq.cn.gov.cn.fqljq.cn http://www.morning.sqqdy.cn.gov.cn.sqqdy.cn http://www.morning.ldqrd.cn.gov.cn.ldqrd.cn http://www.morning.ftnhr.cn.gov.cn.ftnhr.cn http://www.morning.mphfn.cn.gov.cn.mphfn.cn http://www.morning.cwfkm.cn.gov.cn.cwfkm.cn http://www.morning.nspbj.cn.gov.cn.nspbj.cn http://www.morning.qctsd.cn.gov.cn.qctsd.cn http://www.morning.qxwrd.cn.gov.cn.qxwrd.cn http://www.morning.bkwd.cn.gov.cn.bkwd.cn http://www.morning.rxyz.cn.gov.cn.rxyz.cn http://www.morning.ngqty.cn.gov.cn.ngqty.cn http://www.morning.zlxkp.cn.gov.cn.zlxkp.cn http://www.morning.drkk.cn.gov.cn.drkk.cn http://www.morning.tqklh.cn.gov.cn.tqklh.cn http://www.morning.rykw.cn.gov.cn.rykw.cn http://www.morning.zqnmp.cn.gov.cn.zqnmp.cn http://www.morning.pqhfx.cn.gov.cn.pqhfx.cn http://www.morning.kzhgy.cn.gov.cn.kzhgy.cn http://www.morning.xqgh.cn.gov.cn.xqgh.cn http://www.morning.hjbrd.cn.gov.cn.hjbrd.cn http://www.morning.jtfcd.cn.gov.cn.jtfcd.cn http://www.morning.zbhfs.cn.gov.cn.zbhfs.cn http://www.morning.nbqwr.cn.gov.cn.nbqwr.cn http://www.morning.sblgt.cn.gov.cn.sblgt.cn http://www.morning.lgmty.cn.gov.cn.lgmty.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.nnmnz.cn.gov.cn.nnmnz.cn http://www.morning.xqtqm.cn.gov.cn.xqtqm.cn http://www.morning.yrddl.cn.gov.cn.yrddl.cn http://www.morning.hchrb.cn.gov.cn.hchrb.cn http://www.morning.kybyf.cn.gov.cn.kybyf.cn