平常我们使用new、malloc在堆区申请一块内存,但由于每次申请的内存大小不一样就会产生很多内存碎片,造成不好管理与浪费的情况。
内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。
这两个问题有一定的相似度,在面向对象程序编程中,对象的创建与析构都是一个较为复杂的过程,较费时间,所以为了提高程序的运行效率尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。
所以我们可以创建一个进程池(线程池),预先放一些进程(线程)进去,要用的时候就直接调用,用完之后再把进程归还给进程池,省下创建删除进程的时间,不过当然就需要额外的开销了。
利用线程池与进程池可以使管理进程与线程的工作交给系统管理,不需要程序员对里面的线程、进程进行管理。
1、需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2、对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3、接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现"OutOfMemory"的错误。
首先说一下多线程的好处:多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
我们知道应用程序创建一个对象,然后销毁对象是很耗费资源的。创建线程,销毁线程,也是如此。因此,我们就预先生成一些线程,等到我们使用的时候在进行调度,于是,一些"池化资源"技术就这样的产生了。
本文所提到服务器程序是指能够接受客户请求并能处理请求的程序,而不只是指那些接受网络客户请求的网络服务器程序。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:
假设在一台服务器完成一项任务的时间为T
T1 创建线程的时间
T2 在线程中执行任务的时间,包括线程间同步所需时间
T3 线程销毁的时间
显然T = T1+T2+T3。注意这是一个极度简化的假设。
可以看出T1,T3是多线程本身的带来的开销,我们渴望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在程序中频繁的创建或销毁线程,这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点(T1,T3),而不是优点(并发性)。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。在看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。我们比较利用线程池技术和不利于线程池技术的服务器处理这些请求时所产生的线程总数。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目或者上限(以下简称线程池尺寸),而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池尺寸是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
#ifndef __THREAD_H
#define __THREAD_H
#include
#include
#include
using namespace std;
/**
* 执行任务的类,设置任务数据并执行
*/
class CTask
{
protected:
string m_strTaskName; /** 任务的名称 */
void* m_ptrData; /** 要执行的任务的具体数据 */
public:
CTask(){}
CTask(const string& taskName)
{
m_strTaskName = taskName;
m_ptrData = NULL;
}
virtual ~CTask(){}
virtual int Run() = 0;
void SetData(void* data); /** 设置任务数据 */
};
/**
* 线程池管理类的实现
*/
class CThreadPool
{
private:
static vector m_vecTaskList; /** 任务列表 */
static bool shutdown; /** 线程退出标志 */
int m_iThreadNum; /** 线程池中启动的线程数 */
pthread_t *pthread_id;
static pthread_mutex_t m_pthreadMutex; /** 线程同步锁 */
static pthread_cond_t m_pthreadCond; /** 线程同步的条件变量 */
protected:
static void* ThreadFunc(void * threadData); /** 新线程的线程回调函数 */
static int MoveToIdle(pthread_t tid); /** 线程执行结束后,把自己放入到空闲线程中 */
static int MoveToBusy(pthread_t tid); /** 移入到忙碌线程中去 */
int Create(); /** 创建线程池中的线程 */
public:
CThreadPool(int threadNum = 10);
int AddTask(CTask *task); /** 把任务添加到任务队列中 */
int StopAll(); /** 使线程池中的线程退出 */
int getTaskSize(); /** 获取当前任务队列中的任务数 */
};
#endif
Thread.cpp
#include "Thread.h"
#include
#include "stdlib.h"
void CTask::SetData(void * data)
{
m_ptrData = data;
}
vector CThreadPool::m_vecTaskList; //任务列表
bool CThreadPool::shutdown = false;
pthread_mutex_t CThreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t CThreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;
/**
* 线程池管理类构造函数
*/
CThreadPool::CThreadPool(int threadNum)
{
this->m_iThreadNum = threadNum;
cout << "I will create " << threadNum << " threads" << endl;
Create();
}
/**
* 线程回调函数
*/
void* CThreadPool::ThreadFunc(void* threadData)
{
pthread_t tid = pthread_self();
while (1)
{
pthread_mutex_lock(&m_pthreadMutex);
while (m_vecTaskList.size() == 0 && !shutdown)
{
pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex);
}
if (shutdown)
{
pthread_mutex_unlock(&m_pthreadMutex);
printf("thread %lu will exit\n", pthread_self());
pthread_exit(NULL);
}
printf("tid %lu run\n", tid);
vector::iterator iter = m_vecTaskList.begin();
/**
* 取出一个任务并处理之
*/
CTask* task = *iter;
if (iter != m_vecTaskList.end())
{
task = *iter;
m_vecTaskList.erase(iter);
}
pthread_mutex_unlock(&m_pthreadMutex);
task->Run(); /** 执行任务 */
printf("tid:%lu idle\n", tid);
}
return (void*)0;
}
/**
* 往任务队列里边添加任务并发出线程同步信号
*/
int CThreadPool::AddTask(CTask *task)
{
pthread_mutex_lock(&m_pthreadMutex);
this->m_vecTaskList.push_back(task);
pthread_mutex_unlock(&m_pthreadMutex);
pthread_cond_signal(&m_pthreadCond);
return 0;
}
/**
* 创建线程
*/
int CThreadPool::Create()
{
pthread_id = (pthread_t*)malloc(sizeof(pthread_t) * m_iThreadNum);
for(int i = 0; i < m_iThreadNum; i++)
{
pthread_create(&pthread_id[i], NULL, ThreadFunc, NULL);
}
return 0;
}
/**
* 停止所有线程
*/
int CThreadPool::StopAll()
{
/** 避免重复调用 */
if (shutdown)
{
return -1;
}
printf("Now I will end all threads!!\n");
/** 唤醒所有等待线程,线程池要销毁了 */
shutdown = true;
pthread_cond_broadcast(&m_pthreadCond);
/** 阻塞等待线程退出,否则就成僵尸了 */
for (int i = 0; i < m_iThreadNum; i++)
{
pthread_join(pthread_id[i], NULL);
}
free(pthread_id);
pthread_id = NULL;
/** 销毁条件变量和互斥体 */
pthread_mutex_destroy(&m_pthreadMutex);
pthread_cond_destroy(&m_pthreadCond);
return 0;
}
/**
* 获取当前队列中任务数
*/
int CThreadPool::getTaskSize()
{
return m_vecTaskList.size();
}
main.cpp
#include "Thread.h"
#include
#include
#include
class CMyTask: public CTask
{
public:
CMyTask(){}
inline int Run()
{
printf("%s\n", (char*)this->m_ptrData);
sleep(10);
return 0;
}
};
int main()
{
CMyTask taskObj;
char szTmp[] = "this is the new thread running";
taskObj.SetData((void*)szTmp);
CThreadPool threadPool(10);
for(int i = 0; i < 20; i++)
{
threadPool.AddTask(&taskObj);
}
while(1)
{
printf("there are still %d tasks need to handle\n", threadPool.getTaskSize());
if (threadPool.getTaskSize() == 0)
{
if (threadPool.StopAll() == -1)
{
printf("Now I will exit from main\n");
exit(0);
}
}
sleep(2);
}
return 0;
}
运行结果
I will create 10 threads
tid 4378075136 run
this is the new thread running
tid 4373245952 run
this is the new thread running
tid 4373782528 run
this is the new thread running
tid 4374855680 run
this is the new thread running
tid 4374319104 run
this is the new thread running
tid 4375392256 run
this is the new thread running
tid 4375928832 run
this is the new thread running
tid 4376465408 run
this is the new thread running
tid 4377001984 run
this is the new thread running
tid 4377538560 run
this is the new thread running
there are still 10 tasks need to handle
there are still 10 tasks need to handle
there are still 10 tasks need to handle
there are still 10 tasks need to handle
there are still 10 tasks need to handle
tid:4373782528 idle
tid:4375928832 idle
tid:4375392256 idle
tid:4376465408 idle
tid:4374319104 idle
tid:4373245952 idle
tid:4377001984 idle
tid:4378075136 idle
tid:4374855680 idle
tid 4373782528 run
tid:4377538560 idle
this is the new thread running
tid 4375928832 run
this is the new thread running
tid 4375392256 run
this is the new thread running
tid 4376465408 run
this is the new thread running
tid 4374319104 run
this is the new thread running
tid 4373245952 run
this is the new thread running
tid 4377001984 run
this is the new thread running
tid 4378075136 run
this is the new thread running
tid 4374855680 run
tid 4377538560 run
this is the new thread running
this is the new thread running
there are still 0 tasks need to handle
Now I will end all threads!!
tid:4375928832 idle
tid:4376465408 idle
tid:4375392256 idle
tid:4374319104 idle
tid:4373245952 idle
tid:4377538560 idle
tid:4377001984 idle
tid:4378075136 idle
tid:4373782528 idle
tid:4374855680 idle
thread 4375928832 will exit
thread 4376465408 will exit
thread 4375392256 will exit
thread 4374319104 will exit
thread 4373245952 will exit
thread 4377538560 will exit
thread 4377001984 will exit
thread 4378075136 will exit
thread 4373782528 will exit
thread 4374855680 will exit
there are still 0 tasks need to handle
Now I will exit from main