线程池其实就是把任务队列和工作线程绑到一起,提供一个向任务队列中添加任务的接口,下面的代码为了表达更加清楚没有分成头文件和源文件,仅仅是提供思路。
同步机制利用的互斥锁+条件变量,也可以使用C++11提供的原子数封装的自旋锁+条件变量。两种组合的区别在于,自旋锁比较适合当任务比较简单的时候使用,可以减少陷入内核的次数,但当任务比较复杂,线程需要较长时间等待的时候,自旋锁会把大量时间浪费在忙等待上,此时用互斥锁比较好。
#include<condition_variable>
#include<mutex>
#include<thread>
#include<vector>
#include<queue>
#include<memory>
using namespace std;
class ThreadPool
{
public:
typedef std::function<void()> Task;
explicit ThreadPool(size_t threadnum,size_t tasknum)
:running_(false),
maxQueueSize_(tasknum),
maxThreads_(threadnum)
{ } //用的C++11封装好的线程类,锁,条件变量
~ThreadPool()
{
if(running_)
stop();
}
void stop() //停止,其实没啥用,没指望线程池能正常退出
{
{
lock_guard<mutex>lg(mutex_);
running_=false;
notEmpty_.notify_all();
}
for(auto& thr:threads_)
thr->join();
}
void start() //启动工作队列
{
running_=true;
threads_.reserve(maxThreads_);
for(int i=0;i<maxThreads_;i++)
{
threads_.emplace_back(thread(runInThread,this));
threads_[i]->detach();
}
}
void run(Task task) //外界向任务队列添加任务的接口
{
unique_lock<mutex>ul(mutex_);
notFull_.wait(ul,[this]{ return queue_.size()<maxQueueSize_; });
queue_.push(move(task));
notEmpty_.notify_one();
}
private:
bool isFull()const
{
lock_guard<mutex>lg(mutex_);
return queue_.size()>=maxQueueSize_;
}
void runInThread() //工作线程的任务就是从任务队列中取任务,执行,没有任务就阻塞在take中
{
while(running_)
{
Task task(take());
if(task)
task();
}
}
Task take() //从任务队列中取任务
{
unique_lock<mutex>ul(mutex_);
notEmpty_.wait(ul,[this]{ return queue_.empty(); });
Task task;
if(!queue_.empty())
{
task=queue_.front();
queue_.pop();
notFull_.notify_one();
}
}
mutable mutex mutex_;
condition_variable notEmpty_; //本线程池设置了最大任务数量,所以用了两个条件变量
condition_variable notFull_;
vector<unique_ptr<thread>> threads_; //线程实体
queue<Task> queue_; //任务队列
size_t maxQueueSize_;
size_t maxThreads_;
bool running_;
};