c++封裝線程池

線程池

ThreadPool聲明

class Thread;  
  
class ThreadPool final  
{  
public:  
  ThreadPool(const ThreadPool&) = delete;  
  ThreadPool& operator=(const ThreadPool&) = delete;  
  
  explicit ThreadPool(size_t threadNums);  
  
  ~ThreadPool();  
  
  void start();  
  void stop();  
  
  typedef std::function<void()> TaskFunc;  
  void submitTask(const TaskFunc& task);  
  
private:  
  void _runThread();  
  
  TaskFunc _takeTask();  
  
  size_t _threadNums;  
  bool _running;  
  
  std::vector<std::shared_ptr<Thread>> _threadPool;  
  std::queue<TaskFunc> _tasksQueue;  
  
  Mutex _mutex;  
  Condition _cond;  
};  

說明幾點:

(1)Thread使用前向聲明,減少頭文件的依賴;

(2)當任務隊列中任務爲空時,線程池中的線程要等待任務產生,此時線程處於睡眠狀態,等待條件,應該使用條件變量;當計算任務被提交到任務隊列中,要使用條件變量發送信號,喚醒等待的線程;這裏使用此前博客講述的Condition和Mutex來實現;
(3)線程向任務隊列中提交任務和獲取任務,需要使用互斥量保護任務隊列本身,這裏任務隊列使用stl中queue實現;對於線程池的實現,使用vector<std::shared_ptr<Thread>>來實現;

ThreadPool實現

ThreadPool::ThreadPool(size_t threadNums):  
    _threadNums(threadNums),  
    _running(false),  
    _mutex(),  
    _cond(_mutex)  
{  
  assert(!_running);  
  assert(_threadNums > 0);  
  _threadPool.reserve(_threadNums);  
}  
  
ThreadPool::~ThreadPool()  
{  
  if (_running)  
    stop();  
}  
  
void ThreadPool::start()  
{  
  assert(!_running);  
  assert(_threadNums > 0);  
  
  _running = true;  
  
  for (size_t i = 0; i < _threadNums; ++i)  
    {  
      shared_ptr<Thread> thread = make_shared<Thread>(std::bind(&ThreadPool::_runThread, this));  
      _threadPool.push_back(thread);  
      thread->start();  
    }  
}  
  
void ThreadPool::stop()  
{  
  assert(_running);  
  //important, sure threads exit, othrewise thead->join will wait long time, becasuse the thread will sleep long Time;  
  _running = false;  
  
  _cond.wakeAll();      //impotant  
  
for (auto& thread : _threadPool)      //等待線程結束  
    {  
      thread->join();  
    }  
}  
  
void ThreadPool::submitTask(const TaskFunc& task)  
{  
  {  
    MutexLockGuard lock(_mutex);  
    _tasksQueue.push(task);  
  }  
  
  _cond.wake();  
}  
  
ThreadPool::TaskFunc ThreadPool::_takeTask()  
{  
  {  
    MutexLockGuard lock(_mutex);  
    while ( _running && _tasksQueue.empty())  
      {  
        LOG_INFO << "thread tid [" << CurrentThread::tid() << "] wait";  
        _cond.wait();  
      }  
  }  
  
  MutexLockGuard lock(_mutex);  
  TaskFunc task;  
  if (!_tasksQueue.empty())  
    {  
      task = _tasksQueue.front();  
      _tasksQueue.pop();  
    }  
  
  return task;  
}  
  
void ThreadPool::_runThread()  
{  
  assert(_running);  
  while (_running)  
    {  
      try  
        {  
          TaskFunc task = _takeTask();  
  
          if (task)  
            task();  
        }  
      catch (...)  
        {  
          LOG_SYSERR << "Exception happen in ThreadPool";  
        }  
    }  
}  

說明幾點:

(1)存在ThreadPool對象已經析構,但是線程池中線程未終止,因此在Thread析構函數中,首先要對當前狀態_running進行判斷,若仍爲True,是要執行ThreadPool的stop函數的;

(2)在stop函數中,首先將_running置爲false,然後通知所有線程喚醒,此時所有的線程執行完當前的任務後,都會退出_runThread()函數;在stop最後一部分,要join等待線程池中的各個線程,若不等待ThreadPool已經析構後,std::vector<std::shared_ptr<Thread>> _threadPool也將開始析構,造成Thread析構,這樣此後線程執行任何與Thread相關的操作都將會未定義;因此需要線程退出後,Thread纔開始析構,這樣Thread的生命週期要長於線程的生命週期;

(3)在_takeTask(),線程等待的條件是while ( _running && _tasksQueue.empty()),當_running爲false時,說明此時線程池將要停止,因此線程要退出_runThread()函數,join纔會返回,取到的task有可能爲空的,在_runThread()中在執行task之前,還要判斷if (task)該任務是否爲空,不爲空纔會執行任務;


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章