C++线程池

http://www.codeproject.com/Articles/11976/Win32-Thread-Pool

我在原基础上作了一下修改:线程等待Handle重置和销毁延时问题。

// Filename		: RunObject.h
// Author		: Siddharth Barman
// Date			: 18 Sept 2005
// Description	: Defined interface IRunObject which is passed in to the 
//				  the Thread Pool. The Run() method of this interface is
//				  invoked in the thread.
//				  Derive a class from this struct and write your business logic
//				  here. 
//				  Make sure you create the instance of the derived class on the
//				  heap or a at a global scope.
//				  If you create the object in the heap, you can implement the 
//				  AutoDelete() function to return true. If it returns true, 
//				  the thread pool will use 'delete' to free the object. If 
//				  AutoDelete() returns false, the thread pool will not do 
//				  any memory management. It up to you to do that.
//------------------------------------------------------------------------------
#ifndef IRUN_OBJECT_DEFINED
#define IRUN_OBJECT_DEFINED

class CThreadPool;

struct IRunObject
{
	virtual void Run() = 0;
	virtual bool AutoDelete() = 0;
	virtual ~IRunObject(){};
	CThreadPool* pThreadPool;
};

#endif

// Filename		: ThreadPool.h
// Author		: Siddharth Barman
// Date			: 18 Sept 2005
/* Description	: Defines CThreadPool class. How to use the Thread Pool. First
				  create a CThreadPool object. The default constructor will 
				  create 10 threads in the pool. To defer creation of the pool
				  pass the pool size and false to the sonstructor. 
				  
				  You can use two approaches while working with the thread 
				  pool. 

				  1. To make use of the thread pool, you will need to first 
				  create a function having the following signature
				  DWORD WINAPI ThreadProc(LPVOID); Check the CreateThread 
				  documentation in MSDN to get details. Add this function to	
				  the pool by calling the Run() method and pass in the function 
				  name and a void* pointer to any object you want. The pool will
				  pick up the function passed into Run() method and execute as 
				  threads in the pool become free. 

				  2. Instead of using a function pointer, you can use an object
				  of a class which derives from IRunObject. Pass a pointer to 
				  this object in the Run() method of the thread pool. As threads
				  become free, the thread pool will call the Run() method of you
				  r class. You will also need to write a body for AutoDelete() f
				  unction. If the return value is true, the thread pool will use
				  'delete' to free the object you pass in. If it returns false,  
				  the thread pool will not do anything else to the object after
				  calling the Run() function.
	
				  It is possible to destroy the pool whenever you want by 
				  calling the Destroy() method. If you want to create a new pool
				  call the Create() method. Make sure you have destoryed the 
				  existing pool before creating a new one.
				  
				  By default, the pool uses _beginthreadex() function to create
				  threads. To have the pool use CreateThread() Windows API, just
				  define USE_WIN32API_THREAD. Note: it is better to use the defa
				  ult _beginthreadex(). 
				  
				  If this code works, it was written by Siddharth Barman, email 
				  siddharth_b@yahoo.com. 
				   _____ _                        _  ______           _  
				  |_   _| |                      | | | ___ \         | | 
				    | | | |__  _ __ ___  __ _  __| | | |_/ /__   ___ | | 
				    | | | '_ \| '__/ _ \/ _` |/ _` | |  __/ _ \ / _ \| | 
				    | | | | | | | |  __/ (_| | (_| | | | | (_) | (_) | | 
				    \_/ |_| |_|_|  \___|\__,_|\__,_| \_|  \___/ \___/|_|  
------------------------------------------------------------------------------*/
#ifndef THREAD_POOL_CLASS
#define THREAD_POOL_CLASS
#pragma warning( disable : 4786) // remove irritating STL warnings

#include <windows.h>
#include <list>
#include <map>
#include "RunObject.h"

#define POOL_SIZE		  10
#define SHUTDOWN_EVT_NAME _T("PoolEventShutdown")
#define DELAY_TIME		  3000 	
using namespace std;

// info about functions which require servicing will be saved using this struct.
typedef struct tagFunctionData
{
	LPTHREAD_START_ROUTINE lpStartAddress;
	union 
	{
		IRunObject* runObject;
		LPVOID pData;
	};	
} _FunctionData;

// // info about threads in the pool will be saved using this struct.
typedef struct tagThreadData
{
	bool	bFree;
	HANDLE	WaitHandle;
	HANDLE	hThread;
	DWORD	dwThreadId;
} _ThreadData;

// info about all threads belonging to this pool will be stored in this map
typedef map<DWORD, _ThreadData, less<DWORD>, allocator<_ThreadData> > ThreadMap;

// all functions passed in by clients will be initially stored in this list.
typedef list<_FunctionData, allocator<_FunctionData> > FunctionList;

// this decides whether a function is added to the front or back of the list.
enum ThreadPriority
{
	High,
	Low
};

typedef struct UserPoolData
{
	LPVOID pData;
	CThreadPool* pThreadPool;
} _tagUserPoolData;

enum PoolState
{
	Ready, // has been created
	Destroying, // in the process of getting destroyed, no request is processed / accepted
	Destroyed // Destroyed, no threads are available, request can still be queued
};

class CThreadPool
{
private:
	#ifdef USE_WIN32API_THREAD
	static DWORD WINAPI _ThreadProc(LPVOID);
	#else
	static UINT __stdcall _ThreadProc(LPVOID pParam);
	#endif
	
	FunctionList m_functionList;
	ThreadMap m_threads;

	int		m_nPoolSize;
	int		m_nWaitForThreadsToDieMS; // In milli-seconds
	TCHAR	m_szPoolName[256];
	HANDLE	m_hNotifyShutdown; // notifies threads that a new function 
							   // is added
	volatile PoolState	 m_state;
	volatile static long m_lInstance;
	
	CRITICAL_SECTION m_csFuncList;
	CRITICAL_SECTION m_csThreads;

	bool	GetThreadProc(DWORD dwThreadId, LPTHREAD_START_ROUTINE&, 
						  LPVOID*, IRunObject**); 
	
	void	FinishNotify(DWORD dwThreadId);
	void	BusyNotify(DWORD dwThreadId);
	void	ReleaseMemory();
		
	HANDLE	GetWaitHandle(DWORD dwThreadId);
	HANDLE	GetShutdownHandle();

public:
	CThreadPool(int nPoolSize = POOL_SIZE, bool bCreateNow = true, int nWaitTimeForThreadsToComplete = 5000);
	virtual ~CThreadPool();
	bool	Create();	// creates the thread pool
	void	Destroy();	// destroy the thread pool
	
	int		GetPoolSize();
	void	SetPoolSize(int);
	
	bool	Run(LPTHREAD_START_ROUTINE pFunc, LPVOID pData, 
				ThreadPriority priority = Low);

	bool	Run(IRunObject*, ThreadPriority priority = Low);

	bool	CheckThreadStop();

	int		GetWorkingThreadCount();

	PoolState GetState();
};
//------------------------------------------------------------------------------
#endif


// Filename		: ThreadPool.cpp
// Author		: Siddharth Barman
// Date			: 18 Sept 2005
// Description	: Implementation file for 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
//------------------------------------------------------------------------------

volatile long CThreadPool::m_lInstance = 0;

/* Parameters	: Pointer to a _threadData structure.
   Description	: This is the internal thread function which will run 
				  continuously till the Thread Pool is deleted. Any user thread
				  functions will run from within this function.
*/
#ifdef USE_WIN32API_THREAD
DWORD WINAPI CThreadPool::_ThreadProc(LPVOID pParam)
#else
UINT __stdcall CThreadPool::_ThreadProc(LPVOID pParam)
#endif
{
	DWORD					dwWait;
	CThreadPool*			pool;
  	HANDLE					hThread = GetCurrentThread();
	LPTHREAD_START_ROUTINE  proc;
	LPVOID					data;
	DWORD					dwThreadId = GetCurrentThreadId();
	HANDLE					hWaits[2];
	IRunObject*				runObject;
	bool					bAutoDelete;
	DWORD					dwDelayStartTime;

	ASSERT(pParam != NULL);
	if(NULL == pParam)
	{
		return -1;
	}

	pool = static_cast<CThreadPool*>(pParam);
	hWaits[0] = pool->GetWaitHandle(dwThreadId);
	hWaits[1] = pool->GetShutdownHandle();
	
	loop_here:

	dwWait = WaitForMultipleObjects(2, hWaits, FALSE, INFINITE);

	if(dwWait - WAIT_OBJECT_0 == 0)
	{
		if(pool->CheckThreadStop())
		{
			return 0;
		}

		// a new function was added, go and get it
		if(pool->GetThreadProc(dwThreadId, proc, &data, &runObject))
		{	
			pool->BusyNotify(dwThreadId);
			
			if(proc == NULL)
			{
				// a function object is being used instead of 
				// a function pointer.
				bAutoDelete = runObject->AutoDelete();
				
				runObject->Run();

				// see if we need to free this object
				if(bAutoDelete)
				{
					#ifdef _DEBUG
					TCHAR szMessage[256];
					_stprintf(szMessage, _T("Deleting Run Object of thread , handle = %d, id = %d\n"), 
					 hThread, dwThreadId);
					TRACE(szMessage);
					#endif

					delete runObject;
				}
				else
				{
					#ifdef _DEBUG
					TCHAR szMessage[256];
					_stprintf(szMessage, _T("Not Deleted Run Object of thread , handle = %d, id = %d\n"), 
					 hThread, dwThreadId);
					TRACE(szMessage);
					#endif
				}
			}
			else
			{
				// note: the user's data is wrapped inside UserPoolData object
				proc(data);
				
				// Note: data is created by the pool and is deleted by the pool.
				//       The internal user data is not deleted by the pool.
				UserPoolData* pPoolData = static_cast<UserPoolData*>(data);
				delete pPoolData;	
			}
			dwDelayStartTime=GetTickCount();

		}
		if(GetTickCount()-dwDelayStartTime>DELAY_TIME)
		pool->FinishNotify(dwThreadId); // tell the pool, i am now free

		goto loop_here;
	}
			
	return 0;
}

//------------------------------------------------------------------------------
/* Parameters	: Pool size, indicates the number of threads that will be 
				  available in the queue.
*******************************************************************************/
CThreadPool::CThreadPool(int nPoolSize, bool bCreateNow, int nWaitTimeForThreadsToComplete)
{
	m_state = Destroyed;
	m_nPoolSize = nPoolSize;
	m_nWaitForThreadsToDieMS = nWaitTimeForThreadsToComplete;

	// this is used to protect the shared data like the list and map
	InitializeCriticalSection(&m_csFuncList); 
	InitializeCriticalSection(&m_csThreads); 

	if(bCreateNow)
	{
		if(!Create())
		{
			throw 1;
		}
	}
}

//------------------------------------------------------------------------------

/* Description	: Use this method to create the thread pool. The constructor of
				  this class by default will create the pool. Make sure you 
				  do not call this method without first calling the Destroy() 
				  method to release the existing pool.
   Returns		: true if everything went fine else returns false.
  *****************************************************************************/
bool CThreadPool::Create()
{
	if(m_state != Destroyed)
	{
		// To create a new pool, destory the existing one first
		return false;
	}

	InterlockedIncrement(&CThreadPool::m_lInstance);

	_stprintf(m_szPoolName, _T("Pool%d"), m_lInstance);

	HANDLE		hThread;
	DWORD		dwThreadId;
	_ThreadData ThreadData;
	TCHAR		szEvtName[30];
	UINT		uThreadId;	

	// create the event which will signal the threads to stop
	m_hNotifyShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
	ASSERT(m_hNotifyShutdown != NULL);
	if(!m_hNotifyShutdown)
	{
		return false;
	}

	// create the threads
	for(int nIndex = 0; nIndex < m_nPoolSize; nIndex++)
	{
		_stprintf(szEvtName, _T("PID:%d IID:%d TDX:%d"), GetCurrentProcessId(), CThreadPool::m_lInstance, nIndex /* thread index*/);
				
		#ifdef USE_WIN32API_THREAD
		hThread = CreateThread(NULL, 0, CThreadPool::_ThreadProc, 
							   this, CREATE_SUSPENDED, &dwThreadId);
		ASSERT(NULL != hThread);

		if(NULL == hThread)
		{
			return false;
		}
		#else
		hThread = (HANDLE)_beginthreadex(NULL, 0, CThreadPool::_ThreadProc, this,  
								 CREATE_SUSPENDED, (UINT*)&uThreadId);
		ASSERT(INVALID_HANDLE_VALUE != hThread);

		if(INVALID_HANDLE_VALUE == hThread)
		{
			return false;
		}

		dwThreadId = uThreadId;
		#endif
		
		if(hThread)
		{
			// add the entry to the map of threads
			ThreadData.bFree		= true;
			ThreadData.WaitHandle	= CreateEvent(NULL, TRUE, FALSE, szEvtName);
			ThreadData.hThread		= hThread;
			ThreadData.dwThreadId	= dwThreadId;
		
			m_threads.insert(ThreadMap::value_type(dwThreadId, ThreadData));		

			ResumeThread(hThread); 
		
			#ifdef _DEBUG
			TCHAR szMessage[256];
			_stprintf(szMessage, _T("Thread created, handle = %d, id = %d\n"), 
					  hThread, dwThreadId);
			TRACE(szMessage);
			#endif
		}
		else
		{
			return false;
		}
	}

	m_state = Ready;
	return true;
}
//------------------------------------------------------------------------------

CThreadPool::~CThreadPool()
{
	Destroy();
	ReleaseMemory();	
	DeleteCriticalSection(&m_csFuncList);
	DeleteCriticalSection(&m_csThreads);
}
//------------------------------------------------------------------------------

void CThreadPool::ReleaseMemory()
{
	FunctionList::iterator funcIter;
	
	for(funcIter = m_functionList.begin(); funcIter != m_functionList.end(); funcIter++) 
	{
		if(funcIter->pData != NULL) 
		{
			_FunctionData functionData = (*funcIter);
			UserPoolData* pUserPoolData = static_cast<UserPoolData*>(functionData.pData);

			if(pUserPoolData != NULL)
			{
				delete pUserPoolData;
				pUserPoolData = NULL;
			}
		}
	}

	// empty all collections
	m_functionList.clear();
	m_threads.clear();
}

//------------------------------------------------------------------------------

/* Description	: Use this method to destory the thread pool. The destructor of
				  this class will destory the pool anyway. Make sure you 
				  this method before calling a Create() when an existing pool is 
				  already existing.
   Returns		: void
  *****************************************************************************/
void CThreadPool::Destroy()
{
	if(m_state == Destroying || m_state == Destroyed)
		return;
	
	// Set the state to 'destroying'. Pooled functions might be checking this 
	// state to see if they need to stop.
	m_state = Destroying;
		
	// tell all threads to shutdown.
	ASSERT(NULL != m_hNotifyShutdown);
	SetEvent(m_hNotifyShutdown);

	//Sleep(2000); // give the threads a chance to complete

	//if(GetWorkingThreadCount() > 0)
	//{
	//	// There are still threads in processing mode..
	//	// lets give the thread one last chance to finish based on configured setting
	//	Sleep(m_nWaitForThreadsToDieMS);
	//}
	
	ThreadMap::iterator iter;
	_ThreadData ThreadData;
	
	// walk through the events and threads and close them all
	for(iter = m_threads.begin(); iter != m_threads.end(); iter++)
	{
		CloseHandle(iter->second.WaitHandle);
		WaitForSingleObject(iter->second.hThread,INFINITE);
		CloseHandle(iter->second.hThread);
	}

	// close the shutdown event
	CloseHandle(m_hNotifyShutdown);
	m_hNotifyShutdown = NULL;

	ReleaseMemory(); // free any remaining UserPoolData objects 

	InterlockedDecrement(&CThreadPool::m_lInstance);

	m_state = Destroyed;
}
//------------------------------------------------------------------------------

int CThreadPool::GetPoolSize()
{
	return m_nPoolSize;
}
//------------------------------------------------------------------------------

/* Parameters	: nSize - number of threads in the pool.   
   ****************************************************************************/
void CThreadPool::SetPoolSize(int nSize)
{
	ASSERT(nSize > 0);
	if(nSize <= 0)
	{
		return;
	}

	m_nPoolSize = nSize;
}

//------------------------------------------------------------------------------
HANDLE CThreadPool::GetShutdownHandle()
{
	return m_hNotifyShutdown;
}
//------------------------------------------------------------------------------

/* Parameters	: hThread - Handle of the thread that is invoking this function.
   Return		: A ThreadProc function pointer. This function pointer will be 
			      executed by the actual calling thread.
				  NULL is returned if no functions list is empty.
																			  */
bool CThreadPool::GetThreadProc(DWORD dwThreadId, LPTHREAD_START_ROUTINE& Proc, 
								LPVOID* Data, IRunObject** runObject)
{
	LPTHREAD_START_ROUTINE  lpResult = NULL;
	_FunctionData			FunctionData;
	FunctionList::iterator	iter;

	// get the first function info in the function list	
	EnterCriticalSection(&m_csFuncList);
	
	iter = m_functionList.begin();

	if(iter != m_functionList.end())
	{
		FunctionData = (*iter);

		Proc = FunctionData.lpStartAddress;
		
		if(NULL == Proc) // is NULL for function objects
		{		
			*runObject = static_cast<IRunObject*>(FunctionData.pData);
		}
		else		
		{
			// this is a function pointer
			*Data		= FunctionData.pData;
			runObject	= NULL;
		}		

		m_functionList.pop_front(); // remove the function from the list
	
		LeaveCriticalSection(&m_csFuncList);
		return true;	
	}
	else
	{
		LeaveCriticalSection(&m_csFuncList);
		return false;
	}
}
//------------------------------------------------------------------------------

/* Parameters	: hThread - Handle of the thread that is invoking this function.
   Description	: When ever a thread finishes executing the user function, it 
				  should notify the pool that it has finished executing.      
																			  */
void CThreadPool::FinishNotify(DWORD dwThreadId)
{
	ThreadMap::iterator iter;
	
	EnterCriticalSection(&m_csThreads);

	iter = m_threads.find(dwThreadId);

	if(iter == m_threads.end())	// if search found no elements
	{			
		LeaveCriticalSection(&m_csThreads);
		TRACE(_T("No matching thread found."));
		return;
	}
	else
	{	
		m_threads[dwThreadId].bFree = true;

		#ifdef _DEBUG
		TCHAR szMessage[256];
		_stprintf(szMessage, _T("Thread free, thread id = %d\n"), dwThreadId);
		TRACE(szMessage);
		#endif

		if(!m_functionList.empty())
		{
			// there are some more functions that need servicing, lets do that.
			// By not doing anything here we are letting the thread go back and
			// check the function list and pick up a function and execute it.
			LeaveCriticalSection(&m_csThreads);
			return;
		}		
		else
		{
			// back to sleep, there is nothing that needs servicing.
			LeaveCriticalSection(&m_csThreads);
			ResetEvent(m_threads[dwThreadId].WaitHandle);
		}
	}	
}
//------------------------------------------------------------------------------

void CThreadPool::BusyNotify(DWORD dwThreadId)
{
	ThreadMap::iterator iter;
	
	EnterCriticalSection(&m_csThreads);

	iter = m_threads.find(dwThreadId);

	if(iter == m_threads.end())	// if search found no elements
	{
		LeaveCriticalSection(&m_csThreads);
		ASSERT(!_T("No matching thread found."));
	}
	else
	{		
		m_threads[dwThreadId].bFree = false;		

		#ifdef _DEBUG
		TCHAR szMessage[256];
		_stprintf(szMessage, _T("Thread busy, thread id = %d\n"), dwThreadId);
		TRACE(szMessage);
		#endif

		LeaveCriticalSection(&m_csThreads);
	}	
}
//------------------------------------------------------------------------------

/* Parameters	: pFunc - function pointer of type ThreadProc
				  pData - An LPVOID pointer

   Returns		: true is all goes well.
                  false if the pool is in the process of getting destroyed.
   
   Decription	: This function is to be called by clients which want to make 
				  use of the thread pool.
  *****************************************************************************/
bool CThreadPool::Run(LPTHREAD_START_ROUTINE pFunc, LPVOID pData, 
					  ThreadPriority priority)
{
	if(m_state == Destroying || m_state == Destroyed)
		return false;
	
	_FunctionData funcdata;

	UserPoolData* pPoolData = new UserPoolData();
	
	pPoolData->pData = pData;
	pPoolData->pThreadPool = this;

	funcdata.lpStartAddress = pFunc;
	funcdata.pData			= pPoolData;

	// add it to the list
	EnterCriticalSection(&m_csFuncList);
	if(priority == Low)
	{
		m_functionList.push_back(funcdata);
	}
	else
	{
		m_functionList.push_front(funcdata);
	}
	LeaveCriticalSection(&m_csFuncList);

	// See if any threads are free
	ThreadMap::iterator iter;
	_ThreadData ThreadData;

	EnterCriticalSection(&m_csThreads);
	for(iter = m_threads.begin(); iter != m_threads.end(); iter++)
	{
		ThreadData = (*iter).second;
		
		if(ThreadData.bFree)
		{
			// here is a free thread, put it to work
			m_threads[ThreadData.dwThreadId].bFree = false;			
			SetEvent(ThreadData.WaitHandle); 
			// this thread will now call GetThreadProc() and pick up the next
			// function in the list.
			break;
		}
	}
	LeaveCriticalSection(&m_csThreads);

	return true;
}
//------------------------------------------------------------------------------

/* Parameters	: runObject - Pointer to an instance of class which implements
							  IRunObject interface.
				  priority  - Low or high. Based on this the object will be
							  added to the front or back of the list.
   Returns		: true is all goes well.
                  false if the pool is in the process of getting destroyed.

   Decription	: This function is to be called by clients which want to make 
				  use of the thread pool.
  *****************************************************************************/
bool CThreadPool::Run(IRunObject* runObject, ThreadPriority priority)
{
	if(m_state == Destroying || m_state == Destroyed)
		return false;

	ASSERT(runObject != NULL);
		
	runObject->pThreadPool = this; 

	_FunctionData funcdata;

	funcdata.lpStartAddress = NULL; // NULL indicates a function object is being
									// used instead.
	funcdata.pData			= runObject; // the function object

	// add it to the list
	EnterCriticalSection(&m_csFuncList);
	if(priority == Low)
	{
		m_functionList.push_back(funcdata);
	}
	else
	{
		m_functionList.push_front(funcdata);
	}
	LeaveCriticalSection(&m_csFuncList);

	// See if any threads are free
	ThreadMap::iterator iter;
	_ThreadData ThreadData;

	EnterCriticalSection(&m_csThreads);
	for(iter = m_threads.begin(); iter != m_threads.end(); iter++)
	{
		ThreadData = (*iter).second;
		
		if(ThreadData.bFree)
		{
			// here is a free thread, put it to work
			m_threads[ThreadData.dwThreadId].bFree = false;			
			SetEvent(ThreadData.WaitHandle); 
			// this thread will now call GetThreadProc() and pick up the next
			// function in the list.
			break;
		}
	}

	LeaveCriticalSection(&m_csThreads);

	return true;
}
//------------------------------------------------------------------------------

/* Parameters	: ThreadId - the id of the thread for which the wait handle is 
							 being requested.
   Returns		: NULL if no mathcing thread id is present.
				  The HANDLE which can be used by WaitForXXXObject API.
  *****************************************************************************/
HANDLE CThreadPool::GetWaitHandle(DWORD dwThreadId)
{
	HANDLE hWait;
	ThreadMap::iterator iter;
	
	EnterCriticalSection(&m_csThreads);
	iter = m_threads.find(dwThreadId);
	
	if(iter == m_threads.end())	// if search found no elements
	{
		LeaveCriticalSection(&m_csThreads);
		return NULL;
	}
	else
	{		
		hWait = m_threads[dwThreadId].WaitHandle;
		LeaveCriticalSection(&m_csThreads);
	}	

	return hWait;
}
//------------------------------------------------------------------------------

bool CThreadPool::CheckThreadStop()
{
	// This is function expected to be called by thread functions or IRunObject
	// derived. The expectation is that the code will check this 'property' of
	// the pool and stop it's processing as soon as possible.
	return (m_state == Destroying || m_state == Destroyed);
}

//------------------------------------------------------------------------------
 
int CThreadPool::GetWorkingThreadCount()
{
	// Returns true is 
	ThreadMap::iterator iter;
	_ThreadData ThreadData;
	int nCount = 0;

    for(iter = m_threads.begin(); iter != m_threads.end(); iter++) 
	{
		ThreadData = (*iter).second;

        if(!ThreadData.bFree) 
		{
			nCount++;
		}
	}

    return nCount;
}
//------------------------------------------------------------------------------

PoolState CThreadPool::GetState()
{
	return m_state;
}
//------------------------------------------------------------------------------


使用方法:继承IRunObject或者自己定义一个线程函数,通过CThreadPool的Run函数调用即可。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值