您的位置:首页 > 其它

Whether Thread Pool is Needed for You?

2016-04-25 22:53 579 查看

你到底需不需要内存池?

先问自己两个问题:

是否有很多请求需要重复性的进行处理?

而且每个请求是相互独立的?

你是否需要等待IO操作,或是文件操作?

如果你回答YES,那么你需要一个线程池来帮助你。

我们为什么需要内存池?

通常情况下,IO操作都会需要很长的一段时间才能完成。所以,在一个单线程的应用程序中,在IO操作期间,系统资源都会进行等待。如果使用多线程,效率就会大大的提高。所以我们需要线程池更高效的完成多线程操作。

内存池会有什么样的作用?

一个应用程序其实应该避免频繁的创建和销毁线程。

内存池就是可以帮助管理线程,避免频繁的创建和销毁,更加高效。

线程池的设计

线程池会创建一些特定的线程,并且等待被请求。一旦有请求到达了线程池,一个线程就会被激活并且开始执行。当这个请求完成的时候,这个线程会退回到等待请求的状态。当用户请求destroy的时候,线程池中所有的线程都会退出。

设计图如下:



ThreadPool

这个类将会创建、管理、析构线程池。所以,你的应用程序需要创建ThreadPool类的一个对象。

AbstractRequest

这表示线程池的一个请求。客户端应用程序需要继承这个类,并且根据自己的情况实现线程中要执行的函数。

Logger

记录一些日志,错误等信息。

源代码:

.h

#ifndef _THREAD_POOL_MGR_H_
#define _THREAD_POOL_MGR_H_

#include <windows.h>
#include <list>

namespace TP
{

/**
* Logger - This is base class for the error logger class and it is polymorphic.
*          The users of the ThreadPool create a class which derived from this
*          and override LogError() and LogInfo() for their own error logging mechanism.
*          The default error logging will be in output window.
*/
class Logger
{

public:

// Constructor
Logger(){};
// Destructor
virtual ~Logger(){};
// Log error description.
void LogError( const long lActiveReq_i, const std::wstring& wstrError_i );
// Log information.
void LogInfo( const long lActiveReq_i, const std::wstring& wstrInfo_i );
// Override this function to log errors. Default log will be in output window.
virtual void LogError( const std::wstring& wstrError_i );
// Override this function to log informations. Default log will be in output window.
virtual void LogInfo( const std::wstring& wstrInfo_i );

private:

// Log thread ID, Active thread count and last error.
void PrepareLog( const long lActiveReq_i, std::wstring& wstrLog_io );
};

/**
* SyncObject - The class is a wrapper of Critical section object to provide
*              synchronization for thread pool.
*/
class SyncObject
{

public:
// Constructor
SyncObject()
{
::InitializeCriticalSection( &m_stCriticalSection );
}

// Destructor
~SyncObject()
{
::DeleteCriticalSection( &m_stCriticalSection );
}

// Lock critical section.
bool Lock()
{
::EnterCriticalSection( &m_stCriticalSection );
return true;
}

// Unlock critical section.
bool Unlock()
{
::LeaveCriticalSection( &m_stCriticalSection );
return true;
}

private:
SyncObject( const SyncObject& );
SyncObject& operator = ( const SyncObject& );

private:

// Critical section object.
CRITICAL_SECTION m_stCriticalSection;
};

/**
* AutoLock - This class own synchronization object during construction and
*            release the ownership during the destruction.
*/
class AutoLock
{

public:

/**
* Parameterized constructor
*
* @param       LockObj_i - Synchronization object.
* @return      Nil
* @exception   Nil
* @see         Nil
* @since       1.0
*/
AutoLock( SyncObject& LockObj_i ) : m_pSyncObject( &LockObj_i )
{
if( NULL != m_pSyncObject )
{
m_pSyncObject->Lock();
}
}

/**
* Destructor.
*
* @param       Nil
* @return      Nil
* @exception   Nil
* @see         Nil
* @since       1.0
*/
~AutoLock()
{
if( NULL != m_pSyncObject )
{
m_pSyncObject->Unlock();
m_pSyncObject = NULL;
}
}

private:
SyncObject* m_pSyncObject;
};

/**
* AbstractRequest - This is abstract base class for the request to be processed in thread pool.
*                   and it is polymorphic. The users of the ThreadPool must create a class
*                   which derived from this and override Execute() function.
*/
class AbstractRequest
{

public:
// Constructor
AbstractRequest() : m_bAborted( false ), m_usRequestID( 0u ){}
// Destructor
virtual ~AbstractRequest(){}
// Thread procedure to be override in derived class. This function should return if request aborted.
// Abort request can check by calling IsAborted() function during time consuming operation.
virtual long Execute() = 0;
// Set request ID.
void SetRequestID( unsigned short uRequestID_i )
{
AutoLock LockRequest( m_LockWorkerThread );
m_usRequestID = uRequestID_i;
}
// Get request ID.
unsigned short GetRequestID()
{
AutoLock LockRequest( m_LockWorkerThread );
return m_usRequestID;
}
// Abort the processing of the request.
void Abort()
{
AutoLock LockRequest( m_LockWorkerThread );
m_bAborted = true;
}
// Clear abort flag for re-posting the same request.
void ClearAbortFlag()
{
AutoLock LockRequest( m_LockWorkerThread );
m_bAborted = false;
}

protected:
// Check for the abort request
bool IsAborted()
{
AutoLock LockRequest( m_LockWorkerThread );
return m_bAborted;
}
// Prepare error or information log.
void PrepareLog( std::wstring& wstrLog_io );

protected:
// Synchronization object for resource locking.
SyncObject m_LockWorkerThread;

private:
// Abort flag.
bool m_bAborted;
// Request Identifier.
unsigned short m_usRequestID;

};

/**
* AutoCounter - Increment and decrement counter
*/
class AutoCounter
{

public:
// Constructor.
AutoCounter( unsigned short& usCount_io,
SyncObject& Lock_io ) :
m_usCount( usCount_io ), m_LockThread( Lock_io )
{
AutoLock Lock( m_LockThread );
m_usCount++;
}

// Destructor.
~AutoCounter()
{
AutoLock Lock( m_LockThread );
m_usCount--;
}

private:
// Counter variable.
unsigned short& m_usCount;
// Synchronization object for resource locking.
SyncObject& m_LockThread;
};

typedef std::list<AbstractRequest*> REQUEST_QUEUE;

/**
* ThreadPool - This class create and destroy thread pool based on the request.
*              The requested to be processed can be post to pool as derived object of
*              AbstractRequest. Also a class can be derive from Logger to error and
*              information logging.
*/
class ThreadPool
{

public:
// Constructor.
ThreadPool();
// Destructor.
~ThreadPool();

// Create thread pool with specified number of threads.
bool Create( const unsigned short usThreadCount_i, Logger* pLogger_io = NULL );
// Destroy the existing thread pool.
bool Destroy();
// Post request to thread pool for processing.
bool PostRequest( AbstractRequest* pRequest_io );

private:
AbstractRequest* PopRequest( REQUEST_QUEUE& RequestQueue_io );
bool AddThreads();
bool NotifyThread();
bool ProcessRequests();
bool WaitForRequest();
bool DestroyPool();
bool IsDestroyed();
void SetDestroyFlag( const bool bFlag_i );
void CancelRequests();
void LogError( const std::wstring& wstrError_i );
void LogInfo( const std::wstring& wstrInfo_i );
static UINT WINAPI ThreadProc( LPVOID pParam_i );

private:
ThreadPool( const ThreadPool& );
ThreadPool& operator = ( const ThreadPool& );

private:
// Used for thread pool destruction.
bool m_bDestroyed;
// Hold thread count in the pool.
unsigned short m_usThreadCount;
// Released semaphore count.
unsigned short m_usSemaphoreCount;
// Active thread count.
unsigned short m_lActiveThread;
// Active thread count.
unsigned short m_usPendingReqCount;
// Manage active thread count in pool.
HANDLE m_hSemaphore;
// Hold thread handles.
HANDLE* m_phThreadList;
// Request queue.
REQUEST_QUEUE m_RequestQueue;
// Synchronization object for resource locking.
SyncObject m_LockWorkerThread;
// User defined error and information logger class.
Logger* m_pLogger;
// Default error and information logger.
Logger m_Logger;
};
} // namespace TP

#endif // #ifndef _THREAD_POOL_MGR_H_


.cpp

/**
* @author :    Suresh
*/

#include "ThreadPool.h"
#include <sstream>
#include <iomanip>

namespace TP
{

/**
* Log error description.
*
* @param       lActiveReq_i - Count of active requests.
* @param       wstrError_i  - Error message.
*/
void Logger::LogError( const long lActiveReq_i, const std::wstring& wstrError_i )
{
std::wstring wstrLog( wstrError_i );
PrepareLog( lActiveReq_i, wstrLog );
LogError( wstrLog );
}

/**
* Log information.
*
* @param       lActiveReq_i - Count of active requests.
* @param       wstrInfo_i   - Information message.
*/
void Logger::LogInfo( const long lActiveReq_i, const std::wstring& wstrInfo_i )
{
std::wstring wstrLog( wstrInfo_i );
PrepareLog( lActiveReq_i, wstrLog );
LogInfo( wstrLog );
}

/**
* Override this function to log errors. Default log will be in output window.
*
* @param       wstrError_i  - Error description
*/
void Logger::LogError( const std::wstring& wstrError_i )
{
OutputDebugString( wstrError_i.c_str());
}

/**
* Override this function to log informations. Default log will be in output window.
*
* @param       wstrInfo_i   - Information description.
*/
void Logger::LogInfo( const std::wstring& wstrInfo_i )
{
OutputDebugString( wstrInfo_i.c_str());
}

/**
* Log thread ID, Active thread count and last error.
*
* @param       lActiveReq_i - Active thread count.
* @param       wstrLog_io   - Error or information description
*/
void Logger::PrepareLog( const long lActiveReq_i, std::wstring& wstrLog_io )
{
std::wstringstream wstrmLog;
wstrmLog << L"##TP## [TID=" << std::setfill( L'0' ) << std::setw(8) << ::GetCurrentThreadId()
<< L"] [ACTIVE REQUEST=" << std::setw(4) << lActiveReq_i
<< L"] [LAST ERROR=" << std::setw(4) << ::GetLastError()
<< L"] " << wstrLog_io.c_str() << + L"]";
wstrLog_io = wstrmLog.str();
}

/**
* Prepare error or information log.
*
* @param       wstrLog_io - Log information
*/
void AbstractRequest::PrepareLog( std::wstring& wstrLog_io )
{
std::wstringstream wstrmLog;
wstrmLog << std::setfill( L'0' );
wstrmLog << L"##RQ## [RID=" << std::setw(8) << GetRequestID()
<< L"] [Desc=" << wstrLog_io.c_str() << + L"]";
wstrLog_io = wstrmLog.str();
}

/**
* Constructor
*/
ThreadPool::ThreadPool() : m_bDestroyed( false ),
m_usThreadCount( 0u ),
m_usSemaphoreCount( 0u ),
m_lActiveThread( 0u ),
m_usPendingReqCount( 0u ),
m_hSemaphore( NULL ),
m_phThreadList( NULL ),
m_pLogger( &m_Logger )
{
}

/**
* Destructor
*/
ThreadPool::~ThreadPool()
{
if( NULL != m_phThreadList )
{
if( !Destroy())
{
LogError( L"Destroy() failed" );
}
}
}

/**
* Create thread pool with specified number of threads.
*
* @param       usThreadCount_i - Thread count.
* @param       pLogger_i       - Logger instance to log errors and informations
*/
bool ThreadPool::Create( const unsigned short usThreadCount_i, Logger* pLogger_i )
{
try
{
// Assign logger object. If user not provided then use existing and
// error will be logged in output window.
m_pLogger = ( NULL != pLogger_i ) ? pLogger_i : &m_Logger;
// Check thread pool is initialized already.
if( NULL != m_phThreadList )
{
LogError( L"ThreadPool already created" );
return false;
}
// Validate thread count.
if( 0 == usThreadCount_i )
{
LogError( L"Minimum allowed thread count is one" );
return false;
}
if( usThreadCount_i > 64 )
{
LogError( L"Maximum allowed thread count is 64" );
return false;
}
LogInfo( L"Thread pool creation requested" );

// Initialize values.
m_lActiveThread = 0u;
m_usSemaphoreCount = 0u;
m_usPendingReqCount = 0u;
m_usThreadCount = usThreadCount_i;
// Create semaphore for thread count management.
m_hSemaphore = CreateSemaphore( NULL, 0, m_usThreadCount, NULL );
if( NULL == m_hSemaphore )
{
LogError( L"Semaphore creation failed" );
m_usThreadCount = 0u;
return false;
}
// Create worker threads and make pool active
if( !AddThreads())
{
LogError( L"Threads creation failed" );
Destroy();
return false;
}
SetDestroyFlag( false );
LogInfo( L"Thread pool created successfully" );
return true;
}
catch( ... )
{
LogError( L"Exception occurred in Create()" );
return false;
}
}

/**
* Destroy thread pool.
*/
bool ThreadPool::Destroy()
{
try
{
// Check whether thread pool already destroyed.
if( NULL == m_phThreadList )
{
LogError( L"ThreadPool is already destroyed or not created yet" );
return false;
}
// Cancel all requests.
CancelRequests();
// Set destroyed flag to true for exiting threads.
SetDestroyFlag( true );
// Release remaining semaphores to exit thread.
{
AutoLock LockThread( m_LockWorkerThread );
if( m_lActiveThread < m_usThreadCount )
{
if( NULL == ReleaseSemaphore( m_hSemaphore, m_usThreadCount - m_lActiveThread, NULL ))
{
LogError( L"Failed to release Semaphore" );
return false;
}
}
}
// Wait for destroy completion and clean the thread pool.
if( !DestroyPool())
{
LogError( L"Thread pool destruction failed" );
return false;
}
LogInfo( L"Thread Pool destroyed successfully" );
return true;
}
catch( ... )
{
LogError( L"Exception occurred in Destroy()" );
return false;
}
}

/**
* Post request to thread pool for processing
*
* @param       pRequest_io - Request to be processed.
*/
bool ThreadPool::PostRequest( AbstractRequest* pRequest_io )
{
try
{
AutoLock LockThread( m_LockWorkerThread );
if( NULL == m_phThreadList )
{
LogError( L"ThreadPool is destroyed or not created yet" );
return false;
}
m_RequestQueue.push_back( pRequest_io );
if( m_usSemaphoreCount < m_usThreadCount )
{
// Thread available to process, so notify thread.
if( !NotifyThread())
{
LogError( L"NotifyThread failed" );
// Request notification failed. Try after some time.
m_usPendingReqCount++;
return false;
}
}
else
{
// Thread not available to process.
m_usPendingReqCount++;
}
return true;
}
catch( ... )
{
LogError( L"Exception occurred in PostRequest()" );
return false;
}
}

/**
* Pop request from queue for processing.
*
* @param       RequestQueue_io  - Request queue.
* @return      AbstractRequest* - Request pointer.
*/
AbstractRequest* ThreadPool::PopRequest( REQUEST_QUEUE& RequestQueue_io )
{
AutoLock LockThread( m_LockWorkerThread );
if( !RequestQueue_io.empty())
{
AbstractRequest* pRequest = RequestQueue_io.front();
RequestQueue_io.remove( pRequest );
return pRequest;
}
return 0;
}

/**
* Create specified number of threads. Initial status of threads will be waiting.
*/
bool ThreadPool::AddThreads()
{
try
{
// Allocate memory for all threads.
m_phThreadList = new HANDLE[m_usThreadCount];
if( NULL == m_phThreadList )
{
LogError( L"Memory allocation for thread handle failed" );
return false;
}
// Create worker threads.
DWORD dwThreadID = 0;
for( unsigned short usIdx = 0u; usIdx < m_usThreadCount; usIdx++ )
{
// Create worker thread
m_phThreadList[usIdx] = CreateThread( 0, 0,
reinterpret_cast<LPTHREAD_START_ROUTINE>( ThreadPool::ThreadProc ),
this, 0, &dwThreadID );
if( NULL == m_phThreadList[usIdx] )
{
LogError( L"CreateThread failed" );
return false;
}
}
return true;
}
catch( ... )
{
LogError( L"Exception occurred in AddThreads()" );
return false;
}
}

/**
* Add request to queue and release semaphore by one.
*/
bool ThreadPool::NotifyThread()
{
try
{
AutoLock LockThread( m_LockWorkerThread );
// Release semaphore by one to process this request.
if( NULL == ReleaseSemaphore( m_hSemaphore, 1, NULL ))
{
LogError( L"ReleaseSemaphore failed" );
return false;
}
m_usSemaphoreCount++;
return true;
}
catch( ... )
{
LogError( L"Exception occurred in NotifyThread()" );
m_RequestQueue.pop_back();
return false;
}
}

/**
* Process request in queue.
*/
bool ThreadPool::ProcessRequests()
{
bool bContinue( true );
do
{
try
{
LogInfo( L"Thread WAITING" );
// Wait for request.
if( !WaitForRequest())
{
LogError( L"WaitForRequest() failed" );
continue;
}
// Thread counter.
AutoCounter Counter( m_lActiveThread, m_LockWorkerThread );
LogInfo( L"Thread ACTIVE" );
// Check thread pool destroy request.
if( IsDestroyed())
{
LogInfo( L"Thread EXITING" );
break;
}
// Get request from request queue.
AbstractRequest* pRequest = PopRequest( m_RequestQueue );
if( NULL == pRequest )
{
LogError( L"PopRequest failed" );
continue;
}
// Execute the request.
long lReturn = pRequest->Execute();
if( NULL != lReturn )
{
LogError( L"Request execution failed" );
continue;
}
// Check thread pool destroy request.
if( IsDestroyed())
{
LogInfo( L"Thread EXITING" );
break;
}
AutoLock LockThread( m_LockWorkerThread );
// Inform thread if any pending request.
if( m_usPendingReqCount > 0 )
{
if( m_usSemaphoreCount < m_usThreadCount )
{
// Thread available to process, so notify thread.
if( !NotifyThread())
{
LogError( L"NotifyThread failed" );
continue;
}
m_usPendingReqCount--;
}
}
}
catch( ... )
{
LogError( L"Exception occurred in ProcessRequests()" );
continue;
}
}
while( bContinue );
return true;
}

/**
* Wait for request queuing to thread pool.
*/
bool ThreadPool::WaitForRequest()
{
try
{
// Wait released when requested queued.
DWORD dwReturn = WaitForSingleObject( m_hSemaphore, INFINITE );
if( WAIT_OBJECT_0 != dwReturn )
{
LogError( L"WaitForSingleObject failed" );
return false;
}
AutoLock LockThread( m_LockWorkerThread );
m_usSemaphoreCount--;
// Clear previous error.
::SetLastError( 0 );
return true;
}
catch( ... )
{
LogError( L"Exception occurred in WaitForRequest()" );
return false;
}
}

/**
* Destroy and clean up thread pool.
*/
bool ThreadPool::DestroyPool()
{
try
{
// Wait for the exist of threads.
DWORD dwReturn = WaitForMultipleObjects( m_usThreadCount, m_phThreadList, TRUE, INFINITE );
if( WAIT_OBJECT_0 != dwReturn )
{
LogError( L"WaitForMultipleObjects failed" );
return false;
}
// Close all threads.
for( USHORT uIdx = 0u; uIdx < m_usThreadCount; uIdx++ )
{
if( TRUE != CloseHandle( m_phThreadList[uIdx] ))
{
LogError( L"CloseHandle failed for threads" );
return false;
}
}
// Clear memory allocated for threads.
delete[] m_phThreadList;
m_phThreadList = 0;
// Close the semaphore
if( TRUE != CloseHandle( m_hSemaphore ))
{
LogError( L"CloseHandle failed for semaphore" );
return false;
}
// Clear request queue.
m_RequestQueue.clear();
return true;
}
catch( ... )
{
LogError( L"Exception occurred in DestroyPool()" );
return false;
}
}

/**
* Check for destroy request.
*/
inline bool ThreadPool::IsDestroyed()
{
// Avoid synchronization issues if destroy requested after validation.
AutoLock LockThread( m_LockWorkerThread );
// During thread pool destruction all semaphores are released
// to exit all threads.
return m_bDestroyed;
}

/**
* Set destroy flag
*/
inline void ThreadPool::SetDestroyFlag( const bool bFlag_i )
{
AutoLock LockThread( m_LockWorkerThread );
m_bDestroyed = bFlag_i;
}

/**
* Cancel all processing request in pool.
*/
void ThreadPool::CancelRequests()
{
try
{
// Avoid synchronization issues if destroy requested after validation.
AutoLock LockThread( m_LockWorkerThread );
LogInfo( L"Thread pool destroy requested" );
// Clear main queue.
m_RequestQueue.clear();
}
catch( ... )
{
LogError( L"Exception occurred in CancelRequests()" );
}
}

/**
* Log error in thread pool.
*
* @param       wstrError_i - Error description.
*/
void ThreadPool::LogError( const std::wstring& wstrError_i )
{
if( NULL != m_pLogger )
{
m_pLogger->LogError( m_lActiveThread, wstrError_i );
}
}

/**
* Log information in thread pool.
*
* @param       wstrInfo_i - Information description.
*/
void ThreadPool::LogInfo( const std::wstring& wstrInfo_i )
{
if( NULL != m_pLogger )
{
m_pLogger->LogInfo( m_lActiveThread, wstrInfo_i );
}
}

/**
* worker thread procedure.
*
* @param       pParam_i - ThreadPool instance.
* @return      UINT      - Return 0 on success.
*/
UINT ThreadPool::ThreadProc( LPVOID pParam_i )
{
ThreadPool* pThreadPool = NULL;
try
{
ThreadPool* pThreadPool = reinterpret_cast<ThreadPool*>( pParam_i );
if( NULL == pThreadPool )
{
return 1;
}
if( !pThreadPool->ProcessRequests())
{
pThreadPool->LogError( L"ProcessRequests() failed" );
return 1;
}
return 0;
}
catch( ... )
{
if( NULL !=  pThreadPool )
{
pThreadPool->LogError( L"Exception occurred in ThreadProc()" );
}
return 1;
}
}
} // namespace TP
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: