代码改变世界

线程池(译)

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)

 

如何在你的应用中使用线程池?

  1. 包含ThreadPool.hThreadPool.cpp
  2. 如果有需要,线程池可以打印错误信息到调试窗口,而且这个行为可以通过重载来实现。默认的行为可以从Logger来继承一个类,并重载LogError()和LogInfo()函数。
  3. 用Create()创建线程池,如果有需要的话可以提供Logger
  4. 创建一个继承自AbtractRequest的类,可以实现Execute()函数作为线程函数
  5. 创建继承自AbtractRequest的类实例,并传递给线程此用来处理PostRequest()函数。用户可以无限制的发送请求,但是被激活的请求数量等于线程数量
  6. 一旦处理完成以后,能调用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_
ThreadPool.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
ThreadPool.cpp

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

 *****************************************************************************************************************************************************