来源: http://blog.csdn.net/magictong/article/details/3143947
池化技术 - 简单点来说,就是提前保存大量的资源,以备不时之需,O(∩_∩)O,对于线程,内存,oracle的连接对象等等,这些都是资源,程序中当你创建一个线程或者在堆上申请一块内存时,都涉及到很多系统调用,也是非常消耗CPU的,如果你的程序需要很多类似的工作线程或者需要频繁的申请释放小块内存,如果没有在这方面进行优化,那很有可能这部分代码将会成为影响你整个程序性能的瓶颈。池化技术主要有线程池,内存池,连接池,对象池等等,对象池就是提前创建很多对象,将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,连接池比较典型的有oracle的连接池,了解不深。
下面主要谈谈线程池和内存池,因为这两种技术的通用性和实用性也是比较强的,描述语言C++,其实线程池的话用什么实现都可以。
先来谈谈线程池技术,线程池的框架早就有先辈给我们想好了,也就不用我们去冥思苦想了,我们要做的就是把先辈的想法发现即可。
其实线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池中的某一个睡眠线程,让它来处理客户端的这个请求,当处理完这个请求后,线程又处于睡眠状态。可能你也许会问:为什么要搞得这么麻烦,如果每当客户端有新的请求时,我就创建一个新的线程不就完了?这也许是个不错的方法,因为它能使得你编写代码相对容易一些,但你却忽略了一个重要的问题?性能!尤其是对于服务器程序尤为重要,服务器程序会先初始化很多线程在那里等待,当有客户连接时,就激活其中的一个线程来处理客户请求,对于不支持动态增加的线程池,如果没有等待线程,客户就必学等待,而对于能动态增加线程的线程池,则可以像线程池中新加一个线程。
下面是一个用c++实现的线程池,支持动态增减线程数量:
实现:
-
-
-
-
-
-
-
-
-
- #include "stdafx.h"
- #include "KThreadPool.h"
-
-
-
-
-
-
-
- KThread::KThread(): m_hThread(NULL),
- m_status(THREADPAUSE),
- m_hEvt(0),
- m_hMtx(0)
- {
- }
-
- KThread::~KThread()
- {
- ::CloseHandle(m_hMtx);
- if (::TerminateThread(m_hThread, -1) == 0)
- return;
- }
-
- bool KThread::Init()
- {
- m_hThread = ::CreateThread(
- 0,
- 0,
- (PTHREAD_FUNC_EX)(KThread::RealThreadProc),
- (void*)&m_param,
- CREATE_SUSPENDED,
- &m_id);
-
-
- if (NULL == m_hThread)
- {
- return false;
- }
- m_param.proc = NULL;
- m_param.lpParam = NULL;
- m_param.pThread = this;
-
-
- m_hEvt = ::CreateEvent(0, FALSE, FALSE, 0);
- if (m_hEvt == 0)
- {
- ::CloseHandle(m_hThread);
- return false;
- }
-
- m_hMtx = ::CreateMutex(0, 0, 0);
- if (m_hMtx == 0)
- {
- ::CloseHandle(m_hEvt);
- ::CloseHandle(m_hThread);
- return false;
- }
-
- return true;
- }
-
- bool KThread::Run()
- {
- ::WaitForSingleObject(m_hMtx, INFINITE);
- if (m_status == THREADPAUSE)
- if (::ResumeThread(m_hThread) == -1)
- {
- ::ReleaseMutex(m_hMtx);
- return false;
- }
-
- m_status = THREADRUN;
- ::ReleaseMutex(m_hMtx);
- return true;
- }
-
- bool KThread::Pause()
- {
- ::WaitForSingleObject(m_hMtx, INFINITE);
- if (m_status == THREADRUN)
- if (::SuspendThread(m_hThread) == -1)
- {
- ::ReleaseMutex(m_hMtx);
- return false;
- }
-
- m_status = THREADPAUSE;
- ::ReleaseMutex(m_hMtx);
- return true;
- }
-
- void KThread::Join()
- {
- ::WaitForSingleObject(m_hEvt, INFINITE);
- }
-
- void KThread::SetProc(PTHREAD_FUNC_EX proc, void* lpParam)
- {
- ::WaitForSingleObject(m_hMtx, INFINITE);
- m_param.proc = proc;
- m_param.lpParam = lpParam;
- ::ReleaseMutex(m_hMtx);
- }
-
- DWORD WINAPI KThread::RealThreadProc(void* lpParam)
- {
- PTHREAD_FUNC_EX proc;
- KThreadParam *pp = (KThreadParam*)lpParam;
- while (true)
- {
- proc = pp->proc;
- if (proc)
- (*proc)(pp->lpParam);
- pp->proc = NULL;
- pp->lpParam = NULL;
- pp->pThread->Pause();
- ::SetEvent(pp->pThread->m_hEvt);
- }
- }
-
-
-
-
-
-
-
-
-
- KThreadPool::KThreadPool(int initNum)
- {
- KThread *pt;
- for (int i = 0; i < initNum; i++)
- {
- pt = new KThread;
- if (pt->Init())
- m_lst.push_back(pt);
- }
- }
-
- KThreadPool::~KThreadPool()
- {
- list<KThread*>::iterator itBegin = m_lst.begin();
- list<KThread*>::iterator itEnd = m_lst.end();
- for ( ; itBegin != itEnd; itBegin++)
- {
- delete (*itBegin);
- }
- }
-
- DWORD KThreadPool::DoWork(LPTHREAD_START_ROUTINE proc, void *lpParam, bool run)
- {
- list<KThread*>::iterator itBegin = m_lst.begin();
- list<KThread*>::iterator itEnd = m_lst.end();
- for (; itBegin != itEnd; itBegin++)
- {
- if ((*itBegin)->m_param.proc == NULL && (*itBegin)->m_status == THREADPAUSE)
- {
- (*itBegin)->SetProc(proc, lpParam);
-
-
- if (run)
- (*itBegin)->Run();
- return (*itBegin)->m_id;
- }
- }
-
-
- KThread *pt = new KThread;
- if (pt->Init())
- m_lst.push_back(pt);
- pt->SetProc(proc, lpParam);
- if (run)
- pt->Run();
- return pt->m_id;
- }
-
- bool KThreadPool::Run(DWORD id)
- {
- list<KThread*>::iterator itBegin = m_lst.begin();
- list<KThread*>::iterator itEnd = m_lst.end();
- for (; itBegin != itEnd; itBegin++)
- {
- if ((*itBegin)->m_id == id)
- return ((*itBegin)->Run());
- }
-
- return false;
- }
-
- bool KThreadPool::Pause(DWORD id)
- {
- list<KThread*>::iterator itBegin = m_lst.begin();
- list<KThread*>::iterator itEnd = m_lst.end();
- for (; itBegin != itEnd; itBegin++)
- {
- if ((*itBegin)->m_id == id)
- return ((*itBegin)->Pause());
- }
-
- return false;
- }
-
-
-
-
-
-
-
-
- unsigned KThreadPool::SetSize(unsigned size)
- {
- unsigned nowsize = m_lst.size();
- if (nowsize <= size)
- {
- KThread *pt;
- unsigned inc = size - nowsize;
- for (unsigned i = 0; i < inc; i++)
- {
- pt = new KThread;
- if (pt->Init())
- {
- m_lst.push_back(pt);
- }
- }
- return size;
- }
- else
- {
- unsigned dec = nowsize - size;
- list<KThread*>::iterator it = m_lst.begin();
- list<KThread*>::iterator ite = m_lst.end();
- list<KThread*>::iterator itemp;
- unsigned i = 0;
- for (; it != ite && i < dec;)
- {
- if ((*it)->m_status == THREADPAUSE)
- {
- itemp = it++;
- delete ((*itemp));
- m_lst.erase(itemp);
- i++;
- continue;
- }
- it++;
- }
-
- ::Sleep(100 * i);
- return m_lst.size();
- }
- }
-
-
-
使用:
要定义一个线程函数,然后再将该函数和线程数量设置到内存池中,测试代码如下:
- DWORD WINAPI threadFunc(LPVOID lpThreadParam);
- void CKThreadPoolExDlg::OnBtnCreate()
- {
- static i = 0;
- if (!m_pThreadPool)
- {
- m_pThreadPool = new KThreadPool(1);
- }
-
- m_pThreadPool->DoWork(threadFunc, this, true);
- i++;
- if (i == 4)
- {
- m_pThreadPool->SetSize(10);
- }
- }
-
- void CKThreadPoolExDlg::DoSomething()
- {
-
- while (true)
- {
- ::EnterCriticalSection(&m_cs);
- DWORD id = GetCurrentThreadId();
- CString cstr;
- cstr.Format("线程 %d 正在运行...", id);
- m_listInfo.InsertItem(m_listInfo.GetItemCount(), cstr);
- ::LeaveCriticalSection(&m_cs);
- ::Sleep(400);
- }
- }
-
- DWORD WINAPI threadFunc(LPVOID lpThreadParam)
- {
- CKThreadPoolExDlg *pdlg = (CKThreadPoolExDlg *)lpThreadParam;
- pdlg->DoSomething();
- return 0;
- }
代码的详细注释我就不写了,想弄懂原理的还是好好研究下再使用,我不保证里面没有BUG。
再来看看内存池的原理,我下面的参考资料里面有几篇文章讲得不错,想了解原理的可以去看看。
如何更好的管理在应用程序中内存的使用,同时提高内存使用的效率,这是值得每一个开发人员深思的问题。内存池(Memory pool)提供了一种比较可行的解决方案。首先是创建内存池。这个过程的主要任务是预先分配足够大的内存,形成一个初步的“内存池”。分配内存,也就是用户请求内存时,会返回内存池中一块空闲的内存,并将其标志置为已使用,当然具体细节和方法有很多。释放内存时,不是真正地调用free或是delete的过程,而是把内存放回内存池的过程。在把内存放入内存池的同时,要把标志位置为空闲。最后在应用程序结束时,要把内存池销毁。这里主要做的工作就是把内存池中的每一块内存释放。
使用内存池的好处:
1、减少了内存碎片的产生。这个可以从创建内存池的过程中看出。我们在创建内存池时,分配的都是一块块比较整的内存块,这样可以减少内存碎片的产生。
2、提高了内存的使用效率。这个可以从分配内存和释放内存的过程中看出。每次的分配与释放并不是去调用系统提供的函数或是操作符去操作实际的内存,而是在复用内存池中的内存。
缺点就是很有可能会造成内存的浪费,原因也很明显,开始分配了一大块内存,不是全部都用得到的。
针对内存池,这里有两个实现,头一个很简单,用到了STL的队列来管理内存池指针,而且如果分配的内存没有显示的归还给内存池的话,即使内存池销毁的时候,这块内存也不会被销毁,就会有内存泄露,当然这个程序还可以改进,另外一个程序是根据:《C++应用程序性能优化》中提到的一种方法来实现的,程序见下面:
简单实现(还有很大的改进余地):
-
-
-
-
-
-
-
-
- #ifndef __MEMPOOL_H__
- #define __MEMPOOL_H__
-
-
- #include <queue>
-
- #define MemoryBlockSize 1024
- #define MemoryBlockNum 20
-
- class KMemPool
- {
- public:
-
- explicit KMemPool(int a_memoryBlockNum = MemoryBlockNum,
- int a_memoryBlockSize = MemoryBlockSize);
-
- virtual ~KMemPool();
-
-
- int NewMemBuf(char*& a_recvBuff);
- int DelMemBuf(char* a_recvBuff);
-
- private:
- void _initMemPool(int a_memoryBlockNum, int a_memoryBlockSize);
-
- std::queue<char*> m_memPool;
- };
-
-
-
-
- #endif /* __MEMPOOL_H__ */
实现:
-
-
-
-
-
-
-
-
-
- #include "stdafx.h"
- #include "MemPool.h"
-
-
-
-
- KMemPool::KMemPool(int a_memoryBlockNum ,
- int a_memoryBlockSize )
- {
- _initMemPool(a_memoryBlockNum, a_memoryBlockSize);
- }
-
-
- KMemPool::~KMemPool()
- {
- char* l_tempBuff = NULL;
- while(!m_memPool.empty())
- {
- l_tempBuff = m_memPool.front();
- m_memPool.pop();
- if(l_tempBuff )
- {
-
- delete[] l_tempBuff;
- l_tempBuff = NULL;
- }
- }
- }
-
-
-
-
-
-
-
-
-
- void KMemPool::_initMemPool(int a_memoryBlockNum, int a_memoryBlockSize)
- {
- for(int i = 0; i < a_memoryBlockNum; ++i)
- {
- char* l_tempBuff = new char[a_memoryBlockSize];
- if(l_tempBuff == NULL)
- continue;
-
- m_memPool.push(l_tempBuff);
- }
-
- }
-
-
-
-
-
-
-
-
-
-
- int KMemPool::NewMemBuf(char*& a_memoryBuff)
- {
-
-
- if (m_memPool.empty())
- {
- _initMemPool(MemoryBlockNum, MemoryBlockSize);
- }
-
- a_memoryBuff = m_memPool.front();
- m_memPool.pop();
- if(a_memoryBuff == NULL)
- return -1;
-
- return 0;
- }
-
-
-
-
-
-
-
-
- int KMemPool::DelMemBuf(char* a_memoryBuff)
- {
- m_memPool.push(a_memoryBuff);
- return 0;
- }
-
-
根据《C++应用程序性能优化》实现代码如下:
-
-
-
-
-
-
-
-
- #ifndef __MEMPOOLEX_H__
- #define __MEMPOOLEX_H__
-
-
- #include <Windows.h>
- #define MEMPOOL_ALIGNMENT 4
-
- class KMemBlock
- {
- public:
- KMemBlock(USHORT nTypes = 1, USHORT nUnitSize = 0);
- virtual ~KMemBlock();
-
- void operator delete(void *p, size_t);
- void * operator new (size_t, USHORT nTypes, USHORT nUnitSize);
-
- public:
- USHORT nSize;
- USHORT nFree;
- USHORT nFirst;
-
- KMemBlock *pNext;
- char aData[1];
-
- };
-
- class KMemPoolEx
- {
- public:
- KMemPoolEx(USHORT uUnitSize, USHORT uInitSize, USHORT uGrowSize);
- virtual ~KMemPoolEx();
-
- void* Alloc();
- void Free(void* pFree);
-
- private:
- void _FreeMemeoryBlock(KMemBlock* pMyBlock);
-
- KMemBlock* pBlock;
- USHORT nUnitSize;
- USHORT nInitSize;
- USHORT nGrowSize;
- };
-
-
-
- #endif /* __MEMPOOLEX_H__ */
实现部分:
-
-
-
-
-
-
-
-
-
- #include "stdafx.h"
- #include "MemPoolEx.h"
-
-
-
-
- KMemBlock::KMemBlock (USHORT nTypes, USHORT nUnitSize)
- {
- nFree = nTypes - 1;
- pNext = NULL;
- nSize = nTypes * nUnitSize;
- nFirst = 1;
-
- char* pData = aData;
- for(USHORT i = 1; i < nTypes; i++)
- {
- *(USHORT *)pData = i;
- pData += nUnitSize;
- }
- }
-
-
- KMemBlock::~KMemBlock()
- {
- }
-
-
-
-
-
-
-
-
-
-
- void * KMemBlock::operator new(size_t, USHORT nTypes, USHORT nUnitSize)
- {
- return ::operator new (sizeof(KMemBlock) + nTypes * nUnitSize);
- }
-
-
-
-
-
-
-
-
-
- void KMemBlock::operator delete(void *p, size_t)
- {
- ::operator delete (p);
- }
-
-
-
-
-
-
-
-
-
- KMemPoolEx::KMemPoolEx(USHORT _uUnitSize, USHORT _uInitSize, USHORT _uGrowSize)
- {
- pBlock = NULL;
- nInitSize = _uInitSize;
- nGrowSize = _uGrowSize;
-
- if(_uUnitSize <= 2)
- nUnitSize = 2;
- else if(_uUnitSize > 2 && _uUnitSize <= 4)
- nUnitSize = 4;
- else
- {
- if(_uUnitSize % MEMPOOL_ALIGNMENT == 0)
- nUnitSize = _uUnitSize;
- else
- nUnitSize = (_uUnitSize / MEMPOOL_ALIGNMENT + 1) * MEMPOOL_ALIGNMENT;
- }
- }
-
-
- KMemPoolEx::~KMemPoolEx()
- {
- KMemBlock* pMyBlock = pBlock;
- if(pBlock)
- _FreeMemeoryBlock(pMyBlock);
- }
-
-
-
-
-
-
-
-
- void KMemPoolEx::_FreeMemeoryBlock(KMemBlock *pMyBlock)
- {
- if(pMyBlock->pNext)
- _FreeMemeoryBlock(pMyBlock->pNext);
-
- delete pMyBlock;
- }
-
-
-
-
-
-
-
- void* KMemPoolEx::Alloc()
- {
- if(!pBlock)
- {
-
- pBlock = new (nInitSize, nUnitSize) KMemBlock(nInitSize, nUnitSize);
- return pBlock->aData;
- }
-
-
- KMemBlock *pMyBlock = pBlock;
- while(pMyBlock && !pMyBlock->nFree)
- pMyBlock = pMyBlock->pNext;
-
-
- void *retval;
-
-
- if(pMyBlock)
- {
- pMyBlock->nFree--;
- retval = pMyBlock->aData + nUnitSize * pMyBlock->nFirst;
- pMyBlock->nFirst = *((USHORT*)retval);
- return retval;
- }
-
- else
- {
- if(!nGrowSize)
- return NULL;
- KMemBlock* newBlock = new (nGrowSize, nUnitSize) KMemBlock(nGrowSize, nUnitSize);
- if(!newBlock)
- return NULL;
- newBlock->pNext = pBlock;
- pBlock = newBlock;
- return (void*)(newBlock->aData);
- }
-
- return NULL;
- }
-
-
-
-
-
-
-
-
- void KMemPoolEx::Free(void* pFree)
- {
- KMemBlock* pMyBlock = pBlock;
- KMemBlock* pPreBlock = NULL;
-
-
- while((ULONG)pFree < (ULONG)pMyBlock->aData || (ULONG)pFree > (ULONG)(pMyBlock->aData + pMyBlock->nSize))
- {
- pPreBlock = pMyBlock;
- pMyBlock = pMyBlock->pNext;
-
- if(!pMyBlock)
- return;
- }
-
-
- if(pMyBlock)
- {
- pMyBlock->nFree++;
- *((USHORT*)pFree) = pMyBlock->nFirst;
- pMyBlock->nFirst = (USHORT)(((ULONG)pFree - (ULONG)pMyBlock->aData) / nUnitSize);
-
- if(pMyBlock->nFree * nUnitSize == pMyBlock->nSize)
- {
- if(!pMyBlock->pNext)
- {
- delete pMyBlock;
- if(pPreBlock)
- pPreBlock->pNext = NULL;
- else
- pBlock = NULL;
- }
-
- }
- }
- }
-
-
-
数据库连接池是因为,初始化一个数据库连接是非常消耗系统资源的,因此就在程序开始阶段进行数据库的批量连接,然后把这些连接保存下来,等到要使用的时候才拿出来使用,使用完了再放入连接池中。了解不深就不多说了。
参考资料:
CSDN博客:许式伟 著 内存池(MemPool)技术详解 http://blog.csdn.net/xushiweizh/archive/2006/11/22/1402967.aspx
IBM技术文档:内存池 http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html?ca=drs-cn
Java中数据库连接池原理机制讲解:http://www.68design.net/Development/JSP/20703-1.html