线程池的使用(转载)

来源:互联网 发布:Javascript算法 编辑:程序博客网 时间:2024/04/30 14:17
 
·                                 最近一位高手同事实现了一个线程池,代码质量超一流的说。特把代码发上来,还有相关的线程池的文档。
 
    在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些“池化资源”技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的,本文将介绍的线程池技术同样符合这一思想。
    目前,一些著名的大公司都特别看好这项技术,并早已经在他们的产品中应用该技术。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。
    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:
假设在一台服务器完成一项任务的时间为T
T1 创建线程的时间
T2 在线程中执行任务的时间,包括线程间同步所需时间
T3 线程销毁的时间
显然T = T1+T2+T3。注意这是一个极度简化的假设。
可以看出T1,T3是多线程本身的带来的开销,我们渴望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在程序中频繁的创建或销毁线程,这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点(T1,T3),而不是优点(并发性)。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
 
 
下面是线程池的实现代码:
ThreadPool.h
 
// ThreadPool.h: interface for the CThreadPool class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_)
#define AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <afxmt.h>
#include <afxtempl.h>
struct IJobDesc//
工作
{
public:
 BOOL m_bProcessing;//工作正在处理中
 IJobDesc()
 {
  m_bProcessing = FALSE;
 }
};
struct IWorker// 工人
{
 virtual void ProcessJob(IJobDesc* pJob)=0;
};

class CThreadPool 
{
 friend static unsigned int  __stdcall CThreadPool::ManagerProc(void* p);
 friend static unsigned int  __stdcall CThreadPool::WorkerProc(void* p);
protected:
 enum ThreadPoolStatus { BUSY, IDLE, NORMAL };
public:
 //interface to the outside
 void Start(unsigned short nStatic, unsigned short nmax);
 void Stop(bool bHash=false);
 void AddJob(IJobDesc* pJob, IWorker* pWorker);
 void RemoveJob(IJobDesc* pJob);
 //constructor and destructor
 CThreadPool();
 virtual ~CThreadPool();
protected:
 //interfaces public:
 UINT GetMgrWaitTime() const { return 1000*6; }
private:
 static unsigned int __stdcall ManagerProc(void* p);
 static unsigned int  __stdcall WorkerProc(void* p);
protected:
 //manager thread
 HANDLE m_hMgrThread;
 HANDLE m_hMhtEvent;
 HANDLE    m_hWorkerEvent;
protected:
 //configuration parameters
 mutable UINT m_nNumberOfStaticThreads;
 mutable UINT m_nNumberOfTotalThreads;
protected:
 //helper functions
 void AddThreads();
 void RemoveThreads();
 ThreadPoolStatus GetThreadPoolStatus();
 void ChangeStatus(DWORD threadId, bool status);
 void RemoveThread(DWORD threadId)
 {
  CSingleLock lock(&m_arrayCs);
  lock.Lock();
  m_threadMap.RemoveKey(threadId);
  m_bRemovingThread = FALSE;
  lock.Unlock();
 }
 BOOL GetNextProcessJob(IJobDesc ** pJob, IWorker** pWorker);
 
protected:
 
 struct ThreadInfo
 {
  HANDLE m_hThread;
  bool m_bBusyWorking;
  ThreadInfo() { m_hThread=0; m_bBusyWorking=false; }
  ThreadInfo(HANDLE handle, bool bBusy) { m_hThread=handle; m_bBusyWorking=bBusy; }
  ThreadInfo(const ThreadInfo& info) { m_hThread=info.m_hThread; m_bBusyWorking=info.m_bBusyWorking; }
 };
 
 typedef struct JobInfo
 {
  IJobDesc * pJob;
  IWorker * pWorker;
 }JOBINFO;
 CArray<JobInfo,JobInfo&>m_JobList;
 CCriticalSection m_sJoblistSection;
 int m_nCurProcessingJobIndex;
 CCriticalSection m_IndexSection;
 //all the work threads
 CMap<DWORD, DWORD&, ThreadInfo, ThreadInfo&> m_threadMap;
 CCriticalSection m_arrayCs;
 BOOL m_bRemovingThread;
};
#endif // !defined(AFX_THREADPOOL_H__E60DFA90_8F3E_11D4_8AF0_0000C03A07C8__INCLUDED_)
文章引用自:
·                                 ThreadPool.cpp
 
// ThreadPool.cpp: implementation of the CThreadPool class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ThreadPool.h"
#include "process.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CThreadPool::CThreadPool()
{
 m_nCurProcessingJobIndex = -1;
 m_bRemovingThread = FALSE;
 m_JobList.RemoveAll();
}
CThreadPool::~CThreadPool()
{
 m_JobList.RemoveAll();
}
void CThreadPool::Start(unsigned short nStatic, unsigned short nMax)
{
 ASSERT(nMax>=nStatic);
 m_nNumberOfStaticThreads=nStatic;
 m_nNumberOfTotalThreads=nMax;
 m_hMhtEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 m_hWorkerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
 //lock the resource
 CSingleLock singleLock(&m_arrayCs);
 singleLock.Lock();  // Attempt to lock the shared resource
 UINT dwThreadId;
 m_hMgrThread = (HANDLE)_beginthreadex(NULL, 0, &ManagerProc, this, 0, (unsigned*)&dwThreadId);
 for(long n = 0; n < nStatic; n++)
 {
  DWORD threadId;
  HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, &WorkerProc, this, 0, (unsigned*)&threadId);
  TRACE1("generate a worker thread handle id is %d./n", threadId);
  m_threadMap.SetAt(threadId, ThreadInfo(handle, false) );
 }
 singleLock.Unlock();
}
void CThreadPool::Stop(bool bHash)
{
 CSingleLock singleLock(&m_arrayCs);
 singleLock.Lock();  // Attempt to lock the shared resource
 SetEvent(m_hMhtEvent);
 WaitForSingleObject(m_hMgrThread, INFINITE);
 CloseHandle(m_hMhtEvent);
 m_hMhtEvent = NULL;

 //shut down all the worker threads
 UINT nCount=m_threadMap.GetCount();
 HANDLE* pThread = new HANDLE[nCount];
 long n=0;
 POSITION pos=m_threadMap.GetStartPosition();
 DWORD threadId; ThreadInfo info;
 while(pos!=NULL)
 {
  m_threadMap.GetNextAssoc(pos, threadId, info);
  pThread[n++]=info.m_hThread;
 }
 singleLock.Unlock();
 SetEvent(m_hWorkerEvent);
 DWORD rc=WaitForMultipleObjects(nCount, pThread, TRUE, 120000);//wait for 2 minutes, then start to kill threads
 CloseHandle(m_hWorkerEvent);
 m_hWorkerEvent = NULL;
 if(rc==WAIT_TIMEOUT&&bHash)
 {
  //some threads not terminated, we have to stop them.
  DWORD exitCode;
  for(int i=0; i<nCount; i++)
   if (::GetExitCodeThread(pThread[i], &exitCode)==STILL_ACTIVE)
    TerminateThread(pThread[i], 99);
 }
 delete[] pThread;
}
unsigned int CThreadPool::ManagerProc(void* p)
{
 //convert the parameter to the server pointer.
 CThreadPool* pServer=(CThreadPool*)p;
 while(WAIT_OBJECT_0 != WaitForSingleObject(pServer->m_hMhtEvent, 6000))
 {
  //time out processing
  TRACE0("Time out processing!/n");
  //the manager will take a look at all the worker's status. The
  if (pServer->GetThreadPoolStatus()==CThreadPool::BUSY)
   pServer->AddThreads();
  if (pServer->GetThreadPoolStatus()==CThreadPool::IDLE)
   pServer->RemoveThreads();
 }
 return 0;
}
unsigned int CThreadPool::WorkerProc(void* p)
{
 //convert the parameter to the server pointer.
 CThreadPool* pServer=(CThreadPool*)p;
 IWorker* pIWorker = NULL;
 IJobDesc* pIJob= NULL;
 DWORD threadId=::GetCurrentThreadId();
// TRACE1("worker thread id is %d./n", threadId);
 while(true)
 {
 
  if(pServer->m_bRemovingThread)
  {
   pServer->RemoveThread(threadId);
   break;
  }
 // TRACE(L"threadId %d:: begin GetTickCount %d /n",threadId,GetTickCount());
  if (WAIT_OBJECT_0 == WaitForSingleObject(pServer->m_hWorkerEvent, 1))
  {
    break;
  }
 // TRACE(L"threadId %d:: end GetTickCount %d /n",threadId,GetTickCount());
  if (pServer->GetNextProcessJob(&pIJob,&pIWorker))
  {
  // TRACE0("worker events comes in!/n");
   //before processing, we need to change the status to busy.
   pServer->ChangeStatus(threadId, true);
   //retrieve the job description and agent pointer
   if (pIJob && pIWorker)
   {
    pIJob->m_bProcessing = TRUE;
    pIWorker->ProcessJob(pIJob);
    pIJob->m_bProcessing = FALSE;
   }
   pServer->ChangeStatus(threadId, false);
  }
 }
 return 0;
}
void CThreadPool::ChangeStatus(DWORD threadId, bool status)
{
 CSingleLock singleLock(&m_arrayCs);
 singleLock.Lock();  // Attempt to lock the shared resource
 //retrieve the current thread handle
 ThreadInfo info;
 m_threadMap.Lookup(threadId, info);
 info.m_bBusyWorking=status;
 m_threadMap.SetAt(threadId, info);
 singleLock.Unlock();
}
void CThreadPool::AddJob(IJobDesc* pJob, IWorker* pWorker)
{
 CThreadPool::JobInfo jobinfo;
 jobinfo.pJob = pJob;
 jobinfo.pWorker = pWorker;
 CSingleLock lock(&m_sJoblistSection,TRUE);
 m_JobList.Add(jobinfo);
}
void CThreadPool::RemoveJob(IJobDesc* pJob)
{
 int size = m_JobList.GetSize();
 JOBINFO jobinfo;
 for (int i = 0; i < size ; i ++)
 {
  jobinfo = m_JobList.GetAt(i);
  if (jobinfo.pJob == pJob)
  {
   CSingleLock lock(&m_sJoblistSection,TRUE);
   m_JobList.RemoveAt(i);
   return ;
  }
 }
}
BOOL CThreadPool::GetNextProcessJob(IJobDesc** pJob, IWorker** pWorker)
{
 int size = m_JobList.GetSize();
 if (size <= 0) return FALSE;
 JOBINFO jobinfo;
 //lock the resource
 CSingleLock singleLock(&m_IndexSection);
 singleLock.Lock();  //
 m_nCurProcessingJobIndex ++;
 if (m_nCurProcessingJobIndex >= size)
  m_nCurProcessingJobIndex = 0;
 jobinfo = m_JobList.GetAt(m_nCurProcessingJobIndex);
 while (jobinfo.pJob && jobinfo.pJob->m_bProcessing && m_nCurProcessingJobIndex < size) {
  m_nCurProcessingJobIndex ++;
  if (m_nCurProcessingJobIndex < size)
      jobinfo = m_JobList.GetAt(m_nCurProcessingJobIndex);
 }
 singleLock.Unlock();
 if (m_nCurProcessingJobIndex >= size) return FALSE;
 *pJob = jobinfo.pJob;
 *pWorker = jobinfo.pWorker;
 return TRUE;
}
void CThreadPool::AddThreads()
{
 int nCount=m_threadMap.GetCount();
 int nTotal=min(nCount+2, m_nNumberOfTotalThreads);
 for(int i=0; i<nTotal-nCount; i++)
 {
  if (i > 1) break;
  DWORD threadId;
  HANDLE handle = (HANDLE)_beginthreadex(NULL, 0, &WorkerProc, this, 0, (unsigned*)&threadId);
 
  TRACE1("generate a worker thread handle id is %d./n", threadId);
  m_threadMap.SetAt(threadId, ThreadInfo(handle, false) );
 }
}
void CThreadPool::RemoveThreads()
{
 if (m_bRemovingThread) return;
 int nCount=m_threadMap.GetCount();
 int nTotal=max(nCount-2, m_nNumberOfStaticThreads);
 
 if (nCount > nTotal)
 {
  CSingleLock lock(&m_arrayCs,TRUE);
  m_bRemovingThread = TRUE;
 }
 
}
CThreadPool::ThreadPoolStatus CThreadPool::GetThreadPoolStatus()
{
 int nTotal = m_threadMap.GetCount();
 POSITION pos=m_threadMap.GetStartPosition();
 DWORD threadId; ThreadInfo info;
 int nCount=0;
 while(pos!=NULL)
 {
  m_threadMap.GetNextAssoc(pos, threadId, info);
  if (info.m_bBusyWorking==true) nCount++;
 }
 if ( nCount/(1.0*nTotal) > 0.8 )
  return BUSY;
 if ( nCount/ (1.0*nTotal) < 0.2 )
  return IDLE;
 return NORMAL;
}
文章引用自:
http://blog.sina.com.cn/s/blog_4ab96a72010009sv.html
 
原创粉丝点击