完整代码在:https://github.com/dyexlzc/wheself/blob/master/lcthread/tpool.cpp
最近查阅了很多资料,在看C11的高级特性,然后开始上手写线程池,因为是第一次写,涉及到的东西也挺多的。主要就是:
1.任务队列的管理:queue
2.线程的建立 thread
3.锁的使用 mutex
4.线程的唤醒 condition_variable
3和4共同来实现对线程的调度。
说说这个的思路吧。
首先,每一个任务都是这样的一个类【我觉得java的这种多线程方式比较灵活,就用了他的设计模式】
class tjob {
public:
virtual void run() { cout << "tjob" << endl; };
};
通过继承并且重写run方法来实现每一个具体任务的功能。我这里用sleep来模拟大量运算
class job2 : public tpool::tjob {
public:
DWORD time;
job2(DWORD time) {
this->time = time;
}
void run() {
//cout << "我要运行"<<this->time<<"ms." << endl;
Sleep(this->time);
}
};
建立好任务以后,加入到线程池里面去
pool.addTask(*new job2(1000));
他就能自动运行啦
主类的设计是这样的【忽略那两个构造函数,他们俩的区别就在于一个给定了线程数,一个自动获取PC上最大物理线程数而已,其他的没什么区别】,我在下面会解释他们的作用
class tpool {
public:
class tjob {
public:
virtual void run() { cout << "tjob" << endl; };
};
queue<tjob*> job;//任务列表
mutex mutex_global,job_mutex;
condition_variable cv;
thread *tlist;
int num_running,n;//任务个数
//bool if_init;
tpool(int const &_n):n(_n) {
//初始化线程池个数
//if_init = false;
num_running = 0;
//job_mutex = new mutex[_n];
tlist = new thread[_n];
for (int i = 0; i < _n; i++) {
tlist[i]=thread([this,i]() {//直接在这里用lambda表达式建立线程
while (1) {
unique_lock<mutex> lock(this->job_mutex);
//while(this->if_init==false)
this->cv.wait(lock, [this] {
return !this->job.empty();
});//等待阻塞线程,直到update中通知状态改变
if (this->job.size() != 0)
{
this->mutex_global.lock();
tjob* job = this->job.front(); this->job.pop();
this->mutex_global.unlock();
lock.unlock();
this->num_running++;
job->run();
this->num_running--;
cout << i<<"线程运行完毕,当前运行"<<this->num_running<<"/"<<this->n<<"任务,任务队列中还剩余"<<this->job.size()<<"个任务未处理" << endl;
//this->if_init = false;
}
//delete this->job_mutex;
}
});
}
}
tpool() {
int _n = this->core_num();
this->n = _n;
//初始化线程池个数
//if_init = false;
num_running = 0;
//job_mutex = new mutex[_n];
tlist = new thread[_n];
for (int i = 0; i < _n; i++) {
tlist[i] = thread([this, i]() {//直接在这里用lambda表达式建立线程
while (1) {
unique_lock<mutex> lock(this->job_mutex);
//while(this->if_init==false)
this->cv.wait(lock, [this] {
return !this->job.empty();
});//等待阻塞线程,直到update中通知状态改变
if (this->job.size() != 0)
{
this->mutex_global.lock();
tjob* job = this->job.front(); this->job.pop();
lock.unlock();
this->num_running++;
this->mutex_global.unlock();
job->run();
this->mutex_global.lock();
this->num_running--;
this->mutex_global.unlock();
cout << i << "线程运行完毕,当前运行" << this->num_running << "/" << this->n << "任务,任务队列中还剩余" << this->job.size() << "个任务未处理" << endl;
//this->if_init = false;
}
//delete this->job_mutex;
}
});
}
}
void addTask(tjob& job) {
//cout << "添加任务" << endl;
this->job.push(&job);
this->update();
}
void update() {
//更新线程状态
this->cv.notify_one();
}
unsigned int core_num(){
return std::thread::hardware_concurrency();
}
};
先说说成员变量
queue<tjob*> job; //任务队列,里面是指针类型的tjob对象,设置为指针类型才能正确调用派生类的run()函数而不是基类的
mutex mutex_global,job_mutex; //两个锁,第一个锁是全局锁,也就是说在一些需要操作变量的地方加上,第二个job_mutex是指任务锁,每个线程都会于这个锁关联,他和condition_variable共同实现有任务时对线程的唤醒
condition_variable cv; //字面翻译为状态变量,他有两个主要函数notify_one(),notify_all();具体可以查看这篇blog:https://www.cnblogs.com/wangshaowei/p/9593201.html
thread *tlist; //使用指针来初始化n个线程,然后重复利用他们
int num_running,n;//任务个数,用来计算和显示给用户看的
tlist = new thread[_n];
for (int i = 0; i < _n; i++) {//根据参数建立N个线程用来复用,减少线程建立-》销毁的开销
tlist[i] = thread([this, i]() {//直接在这里用lambda表达式建立线程
while (1) {//线程在不断监听,但是它会被下面的wait阻塞住,所以不会占用CPU资源
unique_lock<mutex> lock(this->job_mutex);//所有线程公用一个锁,unique_lock
this->cv.wait(lock, [this] {//cv就是condition_variable,当在其他线程的cv唤醒notify_one()以后,众多线程中的某一个线程就会相应到,于是他就从cv.wait处结束阻塞状态,开始向下运行。
return !this->job.empty();
});//等待阻塞线程,直到update中通知状态改变
if (this->job.size() != 0) //判断任务列表中是否有任务,有的话就运行
{
this->mutex_global.lock(); //因为要对queue进行操作所以必须要加锁!
tjob* job = this->job.front(); this->job.pop();
lock.unlock();
this->num_running++;
this->mutex_global.unlock();
job->run(); //得到对象,开始运行任务
this->mutex_global.lock();
this->num_running--;
this->mutex_global.unlock();
cout << i << "线程运行完毕,当前运行" << this->num_running << "/" << this->n << "任务,任务队列中还剩余" << this->job.size() << "个任务未处理" << endl;
//this->if_init = false;
}
//delete this->job_mutex;
}
});