在阅读TeamTalk的Db_proxy_server中发现Team_Talk的数据代理中用到了线程池的相关技术,也叫数据库连接池,文章总结了线程池的一些基本概念和基本原理,文章末尾列出了一个简单的线程池示例代码。
线程池简介
在面向对象的编程中,对象的创建和销毁是很费时间的,因为创建和销毁一个对象需要获取内存资源以及其他一些相关的资源,所以提高服务程序的一个有效手段是尽量减少对象的创建和销毁,特别是对一些很耗资源的对象的创建和销毁。如何利用一个已有的对象来提供服务就是一个需要解决问题的关键,这也是“池化技术”产生的原因,就如数据库连接池技术。通过预先创建的一些线程来服务每一个数据库请求。
多线程技术主要解决处理器单元内执行多个线程的问题,它可以显著减少处理器的空闲时间,提高处理器单元的吞吐能力,如果对多线程应用不当则可能会增加单个任务的处理时间,例如:
假设服务器完成一项任务的时间为T:
T1 创建线程的时间
T2 在线程中执行任务的时间,包括线程同步所需要的时间
T3线程销毁的时间
显然T = T1 +T2 + T3,可以看出T1和T3是线程本身的时间,用户希望减少T1、T3的时间从而减少T的时间,而一些线程的使用者没有关注到这些问题,在应用程序中频繁的创建和销毁消除,导致T1和T3的时间再整个T时间内占有大部分的比例。
线程池技术正是关注如何减少T1和T3的时间从而提升服务器性能的技术,把线程的创建和销毁统一放在应用程序的初始化阶段和垃圾回收阶段,这样服务器在处理客户请求时不再有创建和销毁消除的时间开销,线程池技术不仅调整了T1、T3的时间此外还显著的减少了线程的数目,再看下面的例子:
假设一个WEB服务器需要处理1000000个请求,通常对于每个请求都创建一个单独的线程来处理(这是常见的场景),那么对于该服务器需要创建和销毁1000000个线程,而如果利用线程池技术,线程池中的线程数目固定的,其远远小于服务器处理的请求数目,因此里用线程池技术不会再创建和销毁线程上浪费时间从而提高服务器服务效率。
线程池技术是一种多线程的处理方法,在应用程序启动时创建一个任务队列,然后创建固定数量的线程,所有的线程都是后台线程,每个线程都使用默认堆栈大小,并拥有相同的优先级。在任务到达时分配一个空闲的线程来处理任务,若任务到达时所有线程都处于忙碌状态,则将任务添加到任务队列中,待某个线程空闲后出任务队列中去除任务进行处理。
线程池的应用范围
1、需要大量的线程来完成任务,且每个任务的时间比较小,比如WEB服务器的请求的网址的任务,单个任务小,但是任务数量大。但对于长时间的任务如FTP请求来说线程池效果就没那么理想了,一个FTP请求的时间远大于线程创建和销毁的时间。
2、对性能要求苛刻的服务,比如要求服务器迅速响应客户的请求。
3、接受突发性大量请求,但不至于使服务器创建大量线程的应用。突发性产生大量客户请求,在没有线程池的情况下将产生大量线程,虽然对于大部分OS来说产生大量的线程不是问题,但是短时间内创建大量线程可能是内存耗尽。
线程池的简单实现
一个线程池至少包含以下几个部分:
1、线程池管理器(ThreadPool):用于创建和管理线程
2、工作线程(WorkThread):线程池中的线程,用于处理具体的任务
3、任务队列(Task list):用于存放没有处理的任务,提供一种缓冲机制。
4、任务接口(Task Interface):所以任务必须实现的接口,供工作线程调度任务执行。
下面列出了一个简单的线程池的实现,在该实现中,每个工作线程都拥有一个自己的任务队列,而不是全局的队列,线程池管理器随机往各个线程的任务队列中添加任务,每个线程处理自己的任务队列中的任务。
线程池管理器拥有一个线程集合,且至少拥有创建线程池,销毁线程池,添加任务的功能
ThreadPool.h
#include "CThread.h"
class ThreadPool
{
public:
ThreadPool();
ThreadPool(unsigned int size);
~ThreadPool();
int Init();
void Destory();
void AddTask(CTask* pTask);
private:
unsigned int m_thread_size;
CThread* m_pWorkThread;
};
ThreadPool.cpp
#include "ThreadPool.h"
#include <iostream>
#include <cstdlib>
using namespace std;
ThreadPool::ThreadPool()
{
m_thread_size = 10;
}
ThreadPool::ThreadPool(unsigned int size)
{
m_thread_size = size;
}
ThreadPool::~ThreadPool()
{}
int ThreadPool::Init()
{
m_pWorkThread = new CThread[m_thread_size];
if(!m_pWorkThread)
return 1;
for(int i=0; i<m_thread_size; i++)
{
m_pWorkThread[i].Start();
}
return 0;
}
void ThreadPool::Destory()
{
if(m_pWorkThread)
delete [] m_pWorkThread;
m_thread_size = 0;
}
void ThreadPool::AddTask(CTask * pTask)
{
unsigned int index = random()%m_thread_size;
m_pWorkThread[index].AddTask(pTask);
}
CThread.h
#include <pthread.h>
#include <list>
#include "CTask.h"
class CThreadNotify
{
public:
CThreadNotify(){};
~CThreadNotify(){};
void Lock(){pthread_mutex_lock(&m_mutex);}
void UnLock(){pthread_mutex_unlock(&m_mutex);}
void Wait() { pthread_cond_wait(&m_con,&m_mutex); }
void Signal() {pthread_cond_signal(&m_con); }
private:
pthread_mutex_t m_mutex;
pthread_mutexattr_t m_mutex_attr;
pthread_cond_t m_con;
};
class CThread
{
public:
CThread();
~CThread();
void Start();
void Run();
bool AddTask(CTask* pTask);
static void* StartRoutine(void* arg);
private:
pthread_t m_thread_id;
unsigned int m_task_cnt;
std::list<CTask*> m_task_list;
CThreadNotify* m_th_notify;
};
CThread.cpp
#include "CThread.h"
#include <iostream>
using namespace std;
CThread::CThread()
{
m_thread_id = 0;
m_th_notify = new CThreadNotify();
}
CThread::~CThread()
{
if(m_th_notify)
delete m_th_notify;
}
void CThread::Start()
{
(void)pthread_create(&m_thread_id, NULL, StartRoutine, this);
}
void CThread::Run()
{
for(; ; )
{
m_th_notify->Lock();
if(m_task_list.empty())
{
cout<<"thread " <<m_thread_id <<" task list is empty\n";
m_th_notify->Wait();
}
CTask* pTask = m_task_list.front();
cout<<"Thread "<<m_thread_id<<" run Task "<<pTask->GetTaskId()<<endl;
pTask->Run();
m_task_list.pop_front(); //任务处理完成后退出队列
m_task_cnt--;
m_th_notify->UnLock();
delete pTask;
}
}
void* CThread::StartRoutine(void * arg)
{
CThread* pThread = (CThread*)arg;
pThread->Run();
}
bool CThread::AddTask(CTask* pTask)
{
m_th_notify->Lock();
m_task_list.push_back(pTask);
m_th_notify->Signal();
cout<<"Push Task taskId:"<<pTask->GetTaskId()<<" into thread: "<<m_thread_id<<endl;
m_th_notify->UnLock();
}
CTask:任务类,每个任务类都必须实现Run接口,在Run接口中处理客户请求
class CTask
{
public:
CTask(unsigned int taskId);
~CTask();
void Run();
unsigned int GetTaskId(){return m_task_id;}
private:
unsigned int m_task_id;
};
CTask.cpp
#include "CTask.h"
#include <iostream>
using namespace std;
CTask::CTask(unsigned int taskId)
{
m_task_id = taskId;
}
CTask::~CTask()
{}
void CTask::Run()
{
cout<<"task:"<<m_task_id<<" do Some Work"<<endl;
}
main.cpp
main函数中我们创建了一个拥有5个线程的线程池,并创建20个任务,随机将任务添加到线程的任务队列中,在主线程sleep 10s,然后销毁所有的线程和线程池。
#include "ThreadPool.h"
#include <unistd.h>
int main()
{
ThreadPool* pTheadPool = new ThreadPool(5);
pTheadPool->Init();
for(int i=0; i<20; i++)
{
CTask* pTask = new CTask(i);
pTheadPool->AddTask(pTask);
}
sleep(10);
//可以设置线程池结束条件,需要结束的时候释放内存
pTheadPool->Destory();
delete pTheadPool;
return 0;
}
任务执行情况如下:
Push Task taskId:thread 0 into thread: 30494380163074616128 task list is empty
thread 3057830720 task list is empty
thread 3041045312 task list is empty
thread 3066223424 task list is empty
Push Task taskId:1 into thread: 3066223424
Push Task taskId:2 into thread: 3057830720
Thread 3049438016 run Task 0
task:0 do Some Work
Push Task taskId:thread 33049438016 into thread: task list is empty
Thread 3066223424 run Task 1
task:1 do Some Work
thread 3066223424 task list is empty
Thread 3057830720 run Task 2
task:2 do Some Work
thread 3057830720 task list is empty
3074616128
Push Task taskId:4 into thread: 3049438016
Push Task taskId:5 into thread: 3074616128
Push Task taskId:6 into thread: 3066223424
Push Task taskId:7 into thread: 3057830720
Thread 3049438016 run Task 4
task:4 do Some Work
thread 3049438016 task list is empty
Thread 3074616128 run Task 3
task:3 do Some Work
Thread 3074616128 run Task 5
Thread Thread 3057830720task:5 do Some Work
thread 3074616128 task list is empty
Push Task taskId:8 into thread: 3041045312
run Task 7
task:7 do Some Work
thread 3057830720 task list is empty
Thread 3041045312 run Task 8
task:8 do Some Work
thread 3041045312 task list is empty
3066223424 run Task 6
task:6 do Some Work
thread 3066223424 task list is empty
Push Task taskId:9 into thread: 3066223424
Push Task taskId:10 into thread: 3057830720
Push Task taskId:11 into thread: 3057830720
Push Task taskId:12 into thread: 3074616128
Thread 3066223424 run Task 9
task:9 do Some Work
thread 3066223424 task list is empty
Thread 3074616128 run Task 12
task:12 do Some Work
thread 3074616128 task list is empty
Push Task taskId:13 into thread: 3041045312
Push Task taskId:14 into thread: 3049438016
Push Task taskId:15 into thread: 3066223424
Thread 3041045312 run Task 13
task:13 do Some Work
thread 3041045312 task list is empty
Push Task taskId:16 into thread: 3074616128
Push Task taskId:17 into thread: 3066223424
Push Task taskId:18 into thread: 3057830720
Push Task taskId:19 into thread: 3066223424
Thread 3057830720 run Task 10
task:10 do Some Work
Thread 3057830720 run Task 11
task:11 do Some Work
Thread 3057830720 run Task 18
task:18 do Some Work
thread 3057830720 task list is empty
Thread 3066223424Thread 3074616128 run Task 16
Thread task:16 do Some Work
thread 3074616128 task list is empty
run Task 15
task:15 do Some Work
Thread 3066223424 run Task 17
task:17 do Some Work
Thread 3066223424 run Task 19
task:19 do Some Work
3049438016thread 3066223424 task list is empty
run Task 14
task:14 do Some Work
thread 3049438016 task list is empty
程序没有对打印函数进行互斥,因此存在某个线程正常打印的时候切换到另一个线程中执行,因此各个打印稍微有点乱,但是仔细区分还是可以看出来的。
PS:文中每个线程拥有一个自己的任务队列,而一般情况是所有线程共享一个全局的任务队列,从全局任务队列中取任务处理;此外,工作线程也分为忙碌线程和空闲线程,空闲线程从任务队列中取任务,然后将状态更新为忙碌状态,这些细节文章都暂未实现,有兴趣的可以自行实现,可从作者的github中下载文中的源代码
git地址:https://github.com/hailong0715/CodeSegment/ThreadPool.git
参考文章:http://www.cnblogs.com/easycloud/p/3726089.html
http://www.360doc.com/content/15/0511/14/12726874_469670444.shtml
http://blog.youkuaiyun.com/hsuxu/article/details/8985931