• 线程池(译)


    这是我翻译的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)

     *****************************************************************************************************************************************************
     
     
  • 相关阅读:
    poj 2777 Count Color
    poj 3264 Balanced Lineup
    hdu 1754 I hate it
    poj 3468 A Simple Problem with Integers
    noip 2013 提高组 Day2 部分题解
    [c/c++]指针(2)
    [c/c++]指针(1)
    vijos 1360 八数码问题
    [复习]快速幂算法
    noip 2013 提高组 day1
  • 原文地址:https://www.cnblogs.com/wb-DarkHorse/p/3284208.html
Copyright © 2020-2023  润新知