线程池(译)
2013-11-17 14:04 钱吉 阅读(1477) 评论(0) 编辑 收藏 举报这是我翻译的codeproject上面的一篇文章,题目是:线程池
一 简介
我的应用中,需要将数据备份到不同的地方(CD,USB,网络共享)。如果我只用单线程来做这件事情,需要花2个小时以上。而如何改善性能对我来说是一个挑战,最终我用多线程来实现了这个问题,结果非常可观,只用了30分钟就完成了我的工作。
其实在刚开始开发时,备份耗费了一整天,后来我被迫增加频率到1个小时。现在我发现了一个问题:实际中由于频率过低导致线程频繁的重复创建和销毁。怎么解决这个问题?为什么线程不能被重复利用呢?所以这就是线程池的由来。
- 你是否需要线程池?
- 你有多个请求需要被重复和并行处理吗?
- 每个请求能独立工作吗?
- 你需要等待IO/File操作吗?
如果你的回答是肯定的,那么线程池非常适合你。你的程序会因此具有低耦合性并且易于实现。
二 背景
作为一名码农,你应该至少有过一次使用单线程的经历,我也无例外。现在我需要创建一个线程池并用C++实现,于是我Google并下载了很多例子,但是它们都不凑效,虽然例子很多,但是没有适合我需要的,所以我写下了这边文章。
我为什么需要一个线程次?一般大多数的IO操作(比如文件,磁盘等)比较耗时,所以用单线程的话,很多系统资源(比如内存,处理器等)会处于等待状态,这显然是一种浪费。这时用多线程的话就能有效利用空闲时间,提高内存和处理器利用率。
对于这些情况下的一些应用使用线程池会比较有效:
- 它要求避免线程创建和销毁等操作带来的时间消耗
- 它是并行的,而且会异步分发大量的小任务
- 它会创建和销毁大量的线程(这些线程运行时间比较短)。用线程池的话会减少线程管理的复杂度,以及线程创建销毁负荷
- 它会在后台并行处理一些独立的任务
线程次的创建
线程池会创建指定数量的线程并等待调用请求。一旦收到一个线程使用请求,它会激活一个线程并开始执行。等执行完成以后,这个线程会变成等待状态,然后等待下一次的请求。如果要求请求清除,所有的线程将会退出线程池。
关系图如下所示:
下面是其中的类的介绍:
- ThreadPool
这个类用于创建,管理和销毁线程池。在使用时,你可以创建一个ThreadPool的对象,并可以指定需要创建的线程数量。这里的线程池最大支持64个线程,它也可以减少至最小线程数以避免过度线程切换和系统资源消耗。
- AbstractRequest
这个类代表了对线程池的请求操作。客户端应用应该继承这个类,并重载其Execute()函数。并且你需要利用其提供的临界区对象来保证线程安全。取消操作可以通过IsAborted()函数来判断。把继承的抽象请求发送到线程池让其处理,也可以通过Abort()取消。
- Logger
线程池有错误信息和log记录功能。默认的log输出是在调试窗口。你也可以创建一个继承Logger的自定义的类并重载这个输出,并实现其中的LogError()和LogInfo()函数。然后将Logger类实例在线程池创建的时候传递给它就可以了。
下面这张图说明了如何在你的应用中使用线程池?
应用程序中有一个ThreadPool的实例,当接收到创建请求时,这个类会创建指定数量的线程,每个线程都将等待被调用。一旦接收到请求,其中的一个线程将结束等待状态并开始处理请求。等处理完成以后,线程恢复到等待状态。用户在任何时候都能调用AbstractRequest的Abort()来中止请求过程。线程池在处理完请求后不会删除它。
三 示例
- Create()
// Create thread pool with specified number of threads. bool Create( const unsigned short usThreadCount_i, Logger* pLogger_io = NULL );
这个函数将创建指定数量的线程并进入等待状态。如果指定了Logger,它将会被用来处理错误。这个函数失败时返回false,最大数量的线程数是64
- Destroy()
// Destroy the existing thread pool. bool Destroy();
这个函数中止所有的请求并销毁线程池。失败时返回false。
- PostRequest()
// Post request to thread pool for processing. bool PostRequest( AbstractRequest* pRequest_io );
这个函数发送指定的请求到线程池,失败时返回false
依赖关系
ThreadPool.h 包含了windows.h, list.h (STL) 和 string.h (STL)
如何在你的应用中使用线程池?
- 包含ThreadPool.h和ThreadPool.cpp
- 如果有需要,线程池可以打印错误信息到调试窗口,而且这个行为可以通过重载来实现。默认的行为可以从Logger来继承一个类,并重载LogError()和LogInfo()函数。
- 用Create()创建线程池,如果有需要的话可以提供Logger
- 创建一个继承自AbtractRequest的类,可以实现Execute()函数作为线程函数
- 创建继承自AbtractRequest的类实例,并传递给线程此用来处理PostRequest()函数。用户可以无限制的发送请求,但是被激活的请求数量等于线程数量
- 一旦处理完成以后,能调用Destroy()销毁线程池。
ThreadPoolDemo
是一个应用ThreadPool类的demo。这个线程池针对Windows操作系统,也可以移植到Linux和IOS平台。可以在本文最下面提供的下载链接获取。
这里是ThreadPool的代码:
1 /** 2 * @author : Suresh 3 */ 4 5 #ifndef _THREAD_POOL_MGR_H_ 6 #define _THREAD_POOL_MGR_H_ 7 8 #include <windows.h> 9 #include <list> 10 11 namespace TP 12 { 13 14 /** 15 * Logger - This is base class for the error logger class and it is polymorphic. 16 * The users of the ThreadPool create a class which derived from this 17 * and override LogError() and LogInfo() for their own error logging mechanism. 18 * The default error logging will be in output window. 19 */ 20 class Logger 21 { 22 23 public: 24 25 // Constructor 26 Logger(){}; 27 // Destructor 28 virtual ~Logger(){}; 29 // Log error description. 30 void LogError( const long lActiveReq_i, const std::wstring& wstrError_i ); 31 // Log information. 32 void LogInfo( const long lActiveReq_i, const std::wstring& wstrInfo_i ); 33 // Override this function to log errors. Default log will be in output window. 34 virtual void LogError( const std::wstring& wstrError_i ); 35 // Override this function to log informations. Default log will be in output window. 36 virtual void LogInfo( const std::wstring& wstrInfo_i ); 37 38 private: 39 40 // Log thread ID, Active thread count and last error. 41 void PrepareLog( const long lActiveReq_i, std::wstring& wstrLog_io ); 42 }; 43 44 /** 45 * SyncObject - The class is a wrapper of Critical section object to provide 46 * synchronization for thread pool. 47 */ 48 class SyncObject 49 { 50 51 public: 52 // Constructor 53 SyncObject() 54 { 55 ::InitializeCriticalSection( &m_stCriticalSection ); 56 } 57 58 // Destructor 59 ~SyncObject() 60 { 61 ::DeleteCriticalSection( &m_stCriticalSection ); 62 } 63 64 // Lock critical section. 65 bool Lock() 66 { 67 ::EnterCriticalSection( &m_stCriticalSection ); 68 return true; 69 } 70 71 // Unlock critical section. 72 bool Unlock() 73 { 74 ::LeaveCriticalSection( &m_stCriticalSection ); 75 return true; 76 } 77 78 private: 79 SyncObject( const SyncObject& ); 80 SyncObject& operator = ( const SyncObject& ); 81 82 private: 83 84 // Critical section object. 85 CRITICAL_SECTION m_stCriticalSection; 86 }; 87 88 /** 89 * AutoLock - This class own synchronization object during construction and 90 * release the ownership during the destruction. 91 */ 92 class AutoLock 93 { 94 95 public: 96 97 /** 98 * Parameterized constructor 99 * 100 * @param LockObj_i - Synchronization object. 101 * @return Nil 102 * @exception Nil 103 * @see Nil 104 * @since 1.0 105 */ 106 AutoLock( SyncObject& LockObj_i ) : m_pSyncObject( &LockObj_i ) 107 { 108 if( NULL != m_pSyncObject ) 109 { 110 m_pSyncObject->Lock(); 111 } 112 } 113 114 /** 115 * Destructor. 116 * 117 * @param Nil 118 * @return Nil 119 * @exception Nil 120 * @see Nil 121 * @since 1.0 122 */ 123 ~AutoLock() 124 { 125 if( NULL != m_pSyncObject ) 126 { 127 m_pSyncObject->Unlock(); 128 m_pSyncObject = NULL; 129 } 130 } 131 132 private: 133 SyncObject* m_pSyncObject; 134 }; 135 136 137 /** 138 * AbstractRequest - This is abstract base class for the request to be processed in thread pool. 139 * and it is polymorphic. The users of the ThreadPool must create a class 140 * which derived from this and override Execute() function. 141 */ 142 class AbstractRequest 143 { 144 145 public: 146 // Constructor 147 AbstractRequest() : m_bAborted( false ), m_usRequestID( 0u ){} 148 // Destructor 149 virtual ~AbstractRequest(){} 150 // Thread procedure to be override in derived class. This function should return if request aborted. 151 // Abort request can check by calling IsAborted() function during time consuming operation. 152 virtual long Execute() = 0; 153 // Set request ID. 154 void SetRequestID( unsigned short uRequestID_i ) 155 { 156 AutoLock LockRequest( m_LockWorkerThread ); 157 m_usRequestID = uRequestID_i; 158 } 159 // Get request ID. 160 unsigned short GetRequestID() 161 { 162 AutoLock LockRequest( m_LockWorkerThread ); 163 return m_usRequestID; 164 } 165 // Abort the processing of the request. 166 void Abort() 167 { 168 AutoLock LockRequest( m_LockWorkerThread ); 169 m_bAborted = true; 170 } 171 // Clear abort flag for re-posting the same request. 172 void ClearAbortFlag() 173 { 174 AutoLock LockRequest( m_LockWorkerThread ); 175 m_bAborted = false; 176 } 177 178 protected: 179 // Check for the abort request 180 bool IsAborted() 181 { 182 AutoLock LockRequest( m_LockWorkerThread ); 183 return m_bAborted; 184 } 185 // Prepare error or information log. 186 void PrepareLog( std::wstring& wstrLog_io ); 187 188 protected: 189 // Synchronization object for resource locking. 190 SyncObject m_LockWorkerThread; 191 192 private: 193 // Abort flag. 194 bool m_bAborted; 195 // Request Identifier. 196 unsigned short m_usRequestID; 197 198 }; 199 200 /** 201 * AutoCounter - Increment and decrement counter 202 */ 203 class AutoCounter 204 { 205 206 public: 207 // Constructor. 208 AutoCounter( unsigned short& usCount_io, 209 SyncObject& Lock_io ) : 210 m_usCount( usCount_io ), m_LockThread( Lock_io ) 211 { 212 AutoLock Lock( m_LockThread ); 213 m_usCount++; 214 } 215 216 // Destructor. 217 ~AutoCounter() 218 { 219 AutoLock Lock( m_LockThread ); 220 m_usCount--; 221 } 222 223 private: 224 // Counter variable. 225 unsigned short& m_usCount; 226 // Synchronization object for resource locking. 227 SyncObject& m_LockThread; 228 }; 229 230 231 typedef std::list<AbstractRequest*> REQUEST_QUEUE; 232 233 234 /** 235 * ThreadPool - This class create and destroy thread pool based on the request. 236 * The requested to be processed can be post to pool as derived object of 237 * AbstractRequest. Also a class can be derive from Logger to error and 238 * information logging. 239 */ 240 class ThreadPool 241 { 242 243 public: 244 // Constructor. 245 ThreadPool(); 246 // Destructor. 247 ~ThreadPool(); 248 249 // Create thread pool with specified number of threads. 250 bool Create( const unsigned short usThreadCount_i, Logger* pLogger_io = NULL ); 251 // Destroy the existing thread pool. 252 bool Destroy(); 253 // Post request to thread pool for processing. 254 bool PostRequest( AbstractRequest* pRequest_io ); 255 256 private: 257 AbstractRequest* PopRequest( REQUEST_QUEUE& RequestQueue_io ); 258 bool AddThreads(); 259 bool NotifyThread(); 260 bool ProcessRequests(); 261 bool WaitForRequest(); 262 bool DestroyPool(); 263 bool IsDestroyed(); 264 void SetDestroyFlag( const bool bFlag_i ); 265 void CancelRequests(); 266 void LogError( const std::wstring& wstrError_i ); 267 void LogInfo( const std::wstring& wstrInfo_i ); 268 static UINT WINAPI ThreadProc( LPVOID pParam_i ); 269 270 private: 271 ThreadPool( const ThreadPool& ); 272 ThreadPool& operator = ( const ThreadPool& ); 273 274 private: 275 // Used for thread pool destruction. 276 bool m_bDestroyed; 277 // Hold thread count in the pool. 278 unsigned short m_usThreadCount; 279 // Released semaphore count. 280 unsigned short m_usSemaphoreCount; 281 // Active thread count. 282 unsigned short m_lActiveThread; 283 // Active thread count. 284 unsigned short m_usPendingReqCount; 285 // Manage active thread count in pool. 286 HANDLE m_hSemaphore; 287 // Hold thread handles. 288 HANDLE* m_phThreadList; 289 // Request queue. 290 REQUEST_QUEUE m_RequestQueue; 291 // Synchronization object for resource locking. 292 SyncObject m_LockWorkerThread; 293 // User defined error and information logger class. 294 Logger* m_pLogger; 295 // Default error and information logger. 296 Logger m_Logger; 297 }; 298 } // namespace TP 299 300 #endif // #ifndef _THREAD_POOL_MGR_H_
1 /** 2 * @author : Suresh 3 */ 4 5 #include "ThreadPool.h" 6 #include <sstream> 7 #include <iomanip> 8 9 namespace TP 10 { 11 12 /** 13 * Log error description. 14 * 15 * @param lActiveReq_i - Count of active requests. 16 * @param wstrError_i - Error message. 17 */ 18 void Logger::LogError( const long lActiveReq_i, const std::wstring& wstrError_i ) 19 { 20 std::wstring wstrLog( wstrError_i ); 21 PrepareLog( lActiveReq_i, wstrLog ); 22 LogError( wstrLog ); 23 } 24 25 26 /** 27 * Log information. 28 * 29 * @param lActiveReq_i - Count of active requests. 30 * @param wstrInfo_i - Information message. 31 */ 32 void Logger::LogInfo( const long lActiveReq_i, const std::wstring& wstrInfo_i ) 33 { 34 std::wstring wstrLog( wstrInfo_i ); 35 PrepareLog( lActiveReq_i, wstrLog ); 36 LogInfo( wstrLog ); 37 } 38 39 40 /** 41 * Override this function to log errors. Default log will be in output window. 42 * 43 * @param wstrError_i - Error description 44 */ 45 void Logger::LogError( const std::wstring& wstrError_i ) 46 { 47 OutputDebugString( wstrError_i.c_str()); 48 } 49 50 51 /** 52 * Override this function to log informations. Default log will be in output window. 53 * 54 * @param wstrInfo_i - Information description. 55 */ 56 void Logger::LogInfo( const std::wstring& wstrInfo_i ) 57 { 58 OutputDebugString( wstrInfo_i.c_str()); 59 } 60 61 62 /** 63 * Log thread ID, Active thread count and last error. 64 * 65 * @param lActiveReq_i - Active thread count. 66 * @param wstrLog_io - Error or information description 67 */ 68 void Logger::PrepareLog( const long lActiveReq_i, std::wstring& wstrLog_io ) 69 { 70 std::wstringstream wstrmLog; 71 wstrmLog << L"##TP## [TID=" << std::setfill( L'0' ) << std::setw(8) << ::GetCurrentThreadId() 72 << L"] [ACTIVE REQUEST=" << std::setw(4) << lActiveReq_i 73 << L"] [LAST ERROR=" << std::setw(4) << ::GetLastError() 74 << L"] " << wstrLog_io.c_str() << + L"]"; 75 wstrLog_io = wstrmLog.str(); 76 } 77 78 79 /** 80 * Prepare error or information log. 81 * 82 * @param wstrLog_io - Log information 83 */ 84 void AbstractRequest::PrepareLog( std::wstring& wstrLog_io ) 85 { 86 std::wstringstream wstrmLog; 87 wstrmLog << std::setfill( L'0' ); 88 wstrmLog << L"##RQ## [RID=" << std::setw(8) << GetRequestID() 89 << L"] [Desc=" << wstrLog_io.c_str() << + L"]"; 90 wstrLog_io = wstrmLog.str(); 91 } 92 93 94 /** 95 * Constructor 96 */ 97 ThreadPool::ThreadPool() : m_bDestroyed( false ), 98 m_usThreadCount( 0u ), 99 m_usSemaphoreCount( 0u ), 100 m_lActiveThread( 0u ), 101 m_usPendingReqCount( 0u ), 102 m_hSemaphore( NULL ), 103 m_phThreadList( NULL ), 104 m_pLogger( &m_Logger ) 105 { 106 } 107 108 109 /** 110 * Destructor 111 */ 112 ThreadPool::~ThreadPool() 113 { 114 if( NULL != m_phThreadList ) 115 { 116 if( !Destroy()) 117 { 118 LogError( L"Destroy() failed" ); 119 } 120 } 121 } 122 123 124 /** 125 * Create thread pool with specified number of threads. 126 * 127 * @param usThreadCount_i - Thread count. 128 * @param pLogger_i - Logger instance to log errors and informations 129 */ 130 bool ThreadPool::Create( const unsigned short usThreadCount_i, Logger* pLogger_i ) 131 { 132 try 133 { 134 // Assign logger object. If user not provided then use existing and 135 // error will be logged in output window. 136 m_pLogger = ( NULL != pLogger_i ) ? pLogger_i : &m_Logger; 137 // Check thread pool is initialized already. 138 if( NULL != m_phThreadList ) 139 { 140 LogError( L"ThreadPool already created" ); 141 return false; 142 } 143 // Validate thread count. 144 if( 0 == usThreadCount_i ) 145 { 146 LogError( L"Minimum allowed thread count is one" ); 147 return false; 148 } 149 if( usThreadCount_i > 64 ) 150 { 151 LogError( L"Maximum allowed thread count is 64" ); 152 return false; 153 } 154 LogInfo( L"Thread pool creation requested" ); 155 156 // Initialize values. 157 m_lActiveThread = 0u; 158 m_usSemaphoreCount = 0u; 159 m_usPendingReqCount = 0u; 160 m_usThreadCount = usThreadCount_i; 161 // Create semaphore for thread count management. 162 m_hSemaphore = CreateSemaphore( NULL, 0, m_usThreadCount, NULL ); 163 if( NULL == m_hSemaphore ) 164 { 165 LogError( L"Semaphore creation failed" ); 166 m_usThreadCount = 0u; 167 return false; 168 } 169 // Create worker threads and make pool active 170 if( !AddThreads()) 171 { 172 LogError( L"Threads creation failed" ); 173 Destroy(); 174 return false; 175 } 176 SetDestroyFlag( false ); 177 LogInfo( L"Thread pool created successfully" ); 178 return true; 179 } 180 catch( ... ) 181 { 182 LogError( L"Exception occurred in Create()" ); 183 return false; 184 } 185 } 186 187 188 /** 189 * Destroy thread pool. 190 */ 191 bool ThreadPool::Destroy() 192 { 193 try 194 { 195 // Check whether thread pool already destroyed. 196 if( NULL == m_phThreadList ) 197 { 198 LogError( L"ThreadPool is already destroyed or not created yet" ); 199 return false; 200 } 201 // Cancel all requests. 202 CancelRequests(); 203 // Set destroyed flag to true for exiting threads. 204 SetDestroyFlag( true ); 205 // Release remaining semaphores to exit thread. 206 { 207 AutoLock LockThread( m_LockWorkerThread ); 208 if( m_lActiveThread < m_usThreadCount ) 209 { 210 if( NULL == ReleaseSemaphore( m_hSemaphore, m_usThreadCount - m_lActiveThread, NULL )) 211 { 212 LogError( L"Failed to release Semaphore" ); 213 return false; 214 } 215 } 216 } 217 // Wait for destroy completion and clean the thread pool. 218 if( !DestroyPool()) 219 { 220 LogError( L"Thread pool destruction failed" ); 221 return false; 222 } 223 LogInfo( L"Thread Pool destroyed successfully" ); 224 return true; 225 } 226 catch( ... ) 227 { 228 LogError( L"Exception occurred in Destroy()" ); 229 return false; 230 } 231 } 232 233 234 /** 235 * Post request to thread pool for processing 236 * 237 * @param pRequest_io - Request to be processed. 238 */ 239 bool ThreadPool::PostRequest( AbstractRequest* pRequest_io ) 240 { 241 try 242 { 243 AutoLock LockThread( m_LockWorkerThread ); 244 if( NULL == m_phThreadList ) 245 { 246 LogError( L"ThreadPool is destroyed or not created yet" ); 247 return false; 248 } 249 m_RequestQueue.push_back( pRequest_io ); 250 if( m_usSemaphoreCount < m_usThreadCount ) 251 { 252 // Thread available to process, so notify thread. 253 if( !NotifyThread()) 254 { 255 LogError( L"NotifyThread failed" ); 256 // Request notification failed. Try after some time. 257 m_usPendingReqCount++; 258 return false; 259 } 260 } 261 else 262 { 263 // Thread not available to process. 264 m_usPendingReqCount++; 265 } 266 return true; 267 } 268 catch( ... ) 269 { 270 LogError( L"Exception occurred in PostRequest()" ); 271 return false; 272 } 273 } 274 275 276 /** 277 * Pop request from queue for processing. 278 * 279 * @param RequestQueue_io - Request queue. 280 * @return AbstractRequest* - Request pointer. 281 */ 282 AbstractRequest* ThreadPool::PopRequest( REQUEST_QUEUE& RequestQueue_io ) 283 { 284 AutoLock LockThread( m_LockWorkerThread ); 285 if( !RequestQueue_io.empty()) 286 { 287 AbstractRequest* pRequest = RequestQueue_io.front(); 288 RequestQueue_io.remove( pRequest ); 289 return pRequest; 290 } 291 return 0; 292 } 293 294 295 /** 296 * Create specified number of threads. Initial status of threads will be waiting. 297 */ 298 bool ThreadPool::AddThreads() 299 { 300 try 301 { 302 // Allocate memory for all threads. 303 m_phThreadList = new HANDLE[m_usThreadCount]; 304 if( NULL == m_phThreadList ) 305 { 306 LogError( L"Memory allocation for thread handle failed" ); 307 return false; 308 } 309 // Create worker threads. 310 DWORD dwThreadID = 0; 311 for( unsigned short usIdx = 0u; usIdx < m_usThreadCount; usIdx++ ) 312 { 313 // Create worker thread 314 m_phThreadList[usIdx] = CreateThread( 0, 0, 315 reinterpret_cast<LPTHREAD_START_ROUTINE>( ThreadPool::ThreadProc ), 316 this, 0, &dwThreadID ); 317 if( NULL == m_phThreadList[usIdx] ) 318 { 319 LogError( L"CreateThread failed" ); 320 return false; 321 } 322 } 323 return true; 324 } 325 catch( ... ) 326 { 327 LogError( L"Exception occurred in AddThreads()" ); 328 return false; 329 } 330 } 331 332 333 /** 334 * Add request to queue and release semaphore by one. 335 */ 336 bool ThreadPool::NotifyThread() 337 { 338 try 339 { 340 AutoLock LockThread( m_LockWorkerThread ); 341 // Release semaphore by one to process this request. 342 if( NULL == ReleaseSemaphore( m_hSemaphore, 1, NULL )) 343 { 344 LogError( L"ReleaseSemaphore failed" ); 345 return false; 346 } 347 m_usSemaphoreCount++; 348 return true; 349 } 350 catch( ... ) 351 { 352 LogError( L"Exception occurred in NotifyThread()" ); 353 m_RequestQueue.pop_back(); 354 return false; 355 } 356 } 357 358 359 /** 360 * Process request in queue. 361 */ 362 bool ThreadPool::ProcessRequests() 363 { 364 bool bContinue( true ); 365 do 366 { 367 try 368 { 369 LogInfo( L"Thread WAITING" ); 370 // Wait for request. 371 if( !WaitForRequest()) 372 { 373 LogError( L"WaitForRequest() failed" ); 374 continue; 375 } 376 // Thread counter. 377 AutoCounter Counter( m_lActiveThread, m_LockWorkerThread ); 378 LogInfo( L"Thread ACTIVE" ); 379 // Check thread pool destroy request. 380 if( IsDestroyed()) 381 { 382 LogInfo( L"Thread EXITING" ); 383 break; 384 } 385 // Get request from request queue. 386 AbstractRequest* pRequest = PopRequest( m_RequestQueue ); 387 if( NULL == pRequest ) 388 { 389 LogError( L"PopRequest failed" ); 390 continue; 391 } 392 // Execute the request. 393 long lReturn = pRequest->Execute(); 394 if( NULL != lReturn ) 395 { 396 LogError( L"Request execution failed" ); 397 continue; 398 } 399 // Check thread pool destroy request. 400 if( IsDestroyed()) 401 { 402 LogInfo( L"Thread EXITING" ); 403 break; 404 } 405 AutoLock LockThread( m_LockWorkerThread ); 406 // Inform thread if any pending request. 407 if( m_usPendingReqCount > 0 ) 408 { 409 if( m_usSemaphoreCount < m_usThreadCount ) 410 { 411 // Thread available to process, so notify thread. 412 if( !NotifyThread()) 413 { 414 LogError( L"NotifyThread failed" ); 415 continue; 416 } 417 m_usPendingReqCount--; 418 } 419 } 420 } 421 catch( ... ) 422 { 423 LogError( L"Exception occurred in ProcessRequests()" ); 424 continue; 425 } 426 } 427 while( bContinue ); 428 return true; 429 } 430 431 432 /** 433 * Wait for request queuing to thread pool. 434 */ 435 bool ThreadPool::WaitForRequest() 436 { 437 try 438 { 439 // Wait released when requested queued. 440 DWORD dwReturn = WaitForSingleObject( m_hSemaphore, INFINITE ); 441 if( WAIT_OBJECT_0 != dwReturn ) 442 { 443 LogError( L"WaitForSingleObject failed" ); 444 return false; 445 } 446 AutoLock LockThread( m_LockWorkerThread ); 447 m_usSemaphoreCount--; 448 // Clear previous error. 449 ::SetLastError( 0 ); 450 return true; 451 } 452 catch( ... ) 453 { 454 LogError( L"Exception occurred in WaitForRequest()" ); 455 return false; 456 } 457 } 458 459 460 /** 461 * Destroy and clean up thread pool. 462 */ 463 bool ThreadPool::DestroyPool() 464 { 465 try 466 { 467 // Wait for the exist of threads. 468 DWORD dwReturn = WaitForMultipleObjects( m_usThreadCount, m_phThreadList, TRUE, INFINITE ); 469 if( WAIT_OBJECT_0 != dwReturn ) 470 { 471 LogError( L"WaitForMultipleObjects failed" ); 472 return false; 473 } 474 // Close all threads. 475 for( USHORT uIdx = 0u; uIdx < m_usThreadCount; uIdx++ ) 476 { 477 if( TRUE != CloseHandle( m_phThreadList[uIdx] )) 478 { 479 LogError( L"CloseHandle failed for threads" ); 480 return false; 481 } 482 } 483 // Clear memory allocated for threads. 484 delete[] m_phThreadList; 485 m_phThreadList = 0; 486 // Close the semaphore 487 if( TRUE != CloseHandle( m_hSemaphore )) 488 { 489 LogError( L"CloseHandle failed for semaphore" ); 490 return false; 491 } 492 // Clear request queue. 493 m_RequestQueue.clear(); 494 return true; 495 } 496 catch( ... ) 497 { 498 LogError( L"Exception occurred in DestroyPool()" ); 499 return false; 500 } 501 } 502 503 504 /** 505 * Check for destroy request. 506 */ 507 inline bool ThreadPool::IsDestroyed() 508 { 509 // Avoid synchronization issues if destroy requested after validation. 510 AutoLock LockThread( m_LockWorkerThread ); 511 // During thread pool destruction all semaphores are released 512 // to exit all threads. 513 return m_bDestroyed; 514 } 515 516 517 /** 518 * Set destroy flag 519 */ 520 inline void ThreadPool::SetDestroyFlag( const bool bFlag_i ) 521 { 522 AutoLock LockThread( m_LockWorkerThread ); 523 m_bDestroyed = bFlag_i; 524 } 525 526 527 /** 528 * Cancel all processing request in pool. 529 */ 530 void ThreadPool::CancelRequests() 531 { 532 try 533 { 534 // Avoid synchronization issues if destroy requested after validation. 535 AutoLock LockThread( m_LockWorkerThread ); 536 LogInfo( L"Thread pool destroy requested" ); 537 // Clear main queue. 538 m_RequestQueue.clear(); 539 } 540 catch( ... ) 541 { 542 LogError( L"Exception occurred in CancelRequests()" ); 543 } 544 } 545 546 547 /** 548 * Log error in thread pool. 549 * 550 * @param wstrError_i - Error description. 551 */ 552 void ThreadPool::LogError( const std::wstring& wstrError_i ) 553 { 554 if( NULL != m_pLogger ) 555 { 556 m_pLogger->LogError( m_lActiveThread, wstrError_i ); 557 } 558 } 559 560 561 /** 562 * Log information in thread pool. 563 * 564 * @param wstrInfo_i - Information description. 565 */ 566 void ThreadPool::LogInfo( const std::wstring& wstrInfo_i ) 567 { 568 if( NULL != m_pLogger ) 569 { 570 m_pLogger->LogInfo( m_lActiveThread, wstrInfo_i ); 571 } 572 } 573 574 575 /** 576 * worker thread procedure. 577 * 578 * @param pParam_i - ThreadPool instance. 579 * @return UINT - Return 0 on success. 580 */ 581 UINT ThreadPool::ThreadProc( LPVOID pParam_i ) 582 { 583 ThreadPool* pThreadPool = NULL; 584 try 585 { 586 ThreadPool* pThreadPool = reinterpret_cast<ThreadPool*>( pParam_i ); 587 if( NULL == pThreadPool ) 588 { 589 return 1; 590 } 591 if( !pThreadPool->ProcessRequests()) 592 { 593 pThreadPool->LogError( L"ProcessRequests() failed" ); 594 return 1; 595 } 596 return 0; 597 } 598 catch( ... ) 599 { 600 if( NULL != pThreadPool ) 601 { 602 pThreadPool->LogError( L"Exception occurred in ThreadProc()" ); 603 } 604 return 1; 605 } 606 } 607 } // namespace TP
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
英文原文:http://www.codeproject.com/Articles/637708/Thread-Pool
源代码 :https://files.cnblogs.com/wb-DarkHorse/ThreadPool.zip