線程池
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(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)該任務是否爲空,不爲空纔會執行任務;