目录
2. using Task = function;(void)>
3. void start(int numThreads);//创建线程
4. void ThreadPool::stop()//停止线程执行
(3)用vector的迭代器 | 迭代器iterator实现
5. 函数void ThreadPool::runInthread()
6. 函数void ThreadPool::run(const Task &cb)
实现原理
1. 该线程池的实现实体有两个分别为事务队列和线程队列。
事务队列用双端队列deque封装,存放的为事务(其实为void(void)类型函数);线程池用vector容器封装,vector中存放唯一性智能指针,智能指针指向线程对象(创建线程将其和函数runInthread绑定)。每个线程创建后会先调用设置的回调函数threadInitCallback(事务),接着去事务队列取事务并执行。
2. 对任务队列的操作采取生产者-消费者模型。
定义一个全局锁变量mutex(保证取事务和添加事务时的互斥性!)+两个条件变量empty(负责管理当取事务而事务队列为空时empty加入条件变量等待队列阻塞empty.wait(mtx);当要结束线程时将标志状态running设置为false且唤醒所有阻塞在等待队列的条件变量empty令take()返回空事务结束执行)和full(当要添加事务进事务队列发现事务队列是满的时full.wait(mtx)阻塞;当取出事务后发现事务队列有空间时唤醒阻塞的条件变量full.notify_all();)
代码实现细节
1. 代码实现环境:VSCode平台与Ubuntu环境
VSCode终端窗口编译代码: g++ -std=c++0x -pthread ThreadPool.cpp -o threadpool
2. using Task = function<void(void)>;
using的用法同typedef,此处是定义事务类型Task为void(void)类型函数的别名。function<>为可调用对象包装器,function<void(void)>也可以认为是一个函数类型或函数对象(包装了void()类型的函数)。
代码中的回调函数即为可被调动执行的事务,为事务类型:Task threadInitCallback;
3. void start(int numThreads);//创建线程
该函数主要是启动线程(修改标志:running=true;)且将线程对象和成员函数runInthread绑定并添加进线程集合(threads.push_back(std::unique_ptr<std::thread>(new thread(&ThreadPool::runInthread, this)));)
4. void ThreadPool::stop()//停止线程执行
此函数可以直接调用唯一性智能指针的clear()函数实现对线程对象的清除,但在这之前必须先join()!!!如下:
(1)用C11特性 | 范围for实现
for(auto&x:threads)
{
x->join();
}
(2)用库函数 | for_each()实现
std::for_each(threads.begin(), threads.end(), std::bind(&std::thread::join(), std::placeholders::_1));
此处传递join()为线程成员函数,故需要绑定一个占位参数即为成员函数隐藏的参数this指针。
(3)用vector的迭代器 | 迭代器iterator实现
for (std::vector<std::unique_ptr<std::thread>>::iterator it = threads.begin(); it != threads.end(); ++it)
{
(*it)->join();//(*it)就是取vector中的元素~指向线程对象的智能指针
}
5. 函数void ThreadPool::runInthread()
每个创建的线程均要和该函数绑定执行。该函数一开始执行回调函数(当回调函数已被注册即不为空时,每个新创建的线程都要先调用执行回调函数/可执行对象),接下来从事务队列取事务并执行。
6. 函数void ThreadPool::run(const Task &cb)
如果线程队列为空~代表就只有该进程自身->直接调用运行要添加进事务队列的事务即可。
否则加锁互斥访问事务队列并将传入函数的事务加入事务队列!加入后唤醒因取事务发现事务队列为空而被阻塞的empty条件变量。
代码实现(有详细注释)
1. ThreadPool.hpp
#ifndef THREADPOOL_H
#define THREADPOOL_H
#include<functional> //bind()
#include<mutex>
#include<condition_variable>
#include<thread>
#include<queue>
#include<memory>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
namespace Yang
{
class ThreadPool
{
public:
using Task = function<void(void)>;
private:
std::vector<std::unique_ptr<std::thread>> threads;
std::deque<Task> queue;//事务队列,存放事务(Task类型,void(void))
std::mutex mtx;
std::condition_variable empty;
std::condition_variable full;
std::string name;//线程名(构造函数赋初值!)
Task threadInitCallback;//回调函数 Task仿函数类型 void(void)
size_t maxQueueSize;//事务的个数
bool running;//标志线程是否运行
bool isFull() const;
void runInthread();
Task take();//获取任务
public:
explicit ThreadPool(const string &name = string("ThreadPool"));
~ThreadPool();
void setMaxQueueSize(int maxsize);
void setThreadCallback(const Task &cb);//设置回调函数
void start(int numThreads);//创建线程
void stop();//停止线程
void run(const Task &cb);
};
} // namespace Yang
#endif
2. ThreadPool.cpp
#include"ThreadPool.hpp"
#include<unistd.h>
namespace Yang
{
using Task = function<void(void)>;
bool ThreadPool::isFull() const
{
return maxQueueSize > 0 && maxQueueSize == queue.size();
}
void ThreadPool::runInthread()//执行线程~执行回调函数(执行任务)
{
if(threadInitCallback!=nullptr)
{
threadInitCallback();//每个线程启动后都要执行回调函数!!
}
//接下来从事务队列取事务并执行!
while(running)
{
Task task(take());//定义事务并初始化(取事务)!
if(task!=nullptr)
{
task();
}
}
}
Task ThreadPool::take()//获取任务,先加锁!!(线程执行状态取事务~为空则阻塞;添加事务~唤醒阻塞队列)
{
std::unique_lock<std::mutex> lock(mtx);
while(running&&queue.empty())
{
empty.wait(lock);//阻塞到空的条件变量等待队列
}
Task task; // Task task=nullptr;
if(!running)
return task;//线程处于非运行态~直接返回空事务!!!!
//线程在运行且事务队列不为空
if(!queue.empty())
{
task = queue.front();
queue.pop_front();
// if(maxQueueSize>0) //事务队列还有事务~唤醒阻塞的条件变量
// {
// full.notify_all();
// }
if(queue.size()<maxQueueSize) //如果还有位置~唤醒放入事务时因满阻塞的条件变量!
{
full.notify_all();
}
}
return task;
}
ThreadPool::ThreadPool(const string &name):mtx{},full{},empty{},name{name},maxQueueSize{0},running{false}{ }
ThreadPool::~ThreadPool()//将start()中new的线程对象均释放!
{
if(running)
{
stop();
}
}
void ThreadPool::setMaxQueueSize(int maxsize) //事务队列大小
{
maxQueueSize = maxsize;
}
void ThreadPool::setThreadCallback(const Task &cb)//设置回调函数
{
threadInitCallback = cb;
}
void ThreadPool::start(int numThreads) //启动并设置线程个数
{
running = true;
for (int i = 0; i < numThreads;++i)
{
//启动线程~将线程和runInthread函数绑定(引用,类成员函数传this指针)绑定,执行runInthread函数
threads.push_back(std::unique_ptr<std::thread>(new thread(&ThreadPool::runInthread, this)));
}
if(numThreads==0)
threadInitCallback();//如果线程=0~只有当前进程,直接执行回调函数(可执行对象)
}
void ThreadPool::stop()//停止线程执行
{
{
//避免多线程,start和stop同时执行~应该将对running的改变使用互斥锁封装到块内
std::unique_lock<std::mutex> lock(mtx);
running = false;
//把34行阻塞到空队列的全唤醒!标志线程不运行,让他们执行下一句返回空事务!!
empty.notify_all();
}
//每个线程对象先调用join()
/*for(auto&x:threads)
{
x->join();
}*/
//绑定线程自己的join()函数,成员函数第一个参数为this指针,绑定器绑定!
//std::for_each(threads.begin(), threads.end(), std::bind(&std::thread::join(), std::placeholders::_1));
for (std::vector<std::unique_ptr<std::thread>>::iterator it = threads.begin(); it != threads.end(); ++it)
{
(*it)->join();//(*it)就是取vector中的元素~指向线程对象的智能指针
}
threads.clear(); //调用唯一性智能指针的清除函数~可以实现delete指向的线程对象!(但在这之前必须先join!!!)
}
void ThreadPool::run(const Task &cb) //添加事务并调动事务运行(没线程~直接运行事务;否则若事务队列不满~加锁添加事务)
{
if(threads.empty())
{
cb();
}
std::unique_lock<std::mutex> lock(mtx);
while(isFull())
{
full.wait(lock);
}
queue.push_back(cb);
empty.notify_all();
}
}
#include<iostream>
using namespace std;
void print()
{
cout << "InitThread" << endl;
}
void printString(const string& str)
{
cout << "string:" << str << endl;
}
class Test
{
private:
std::string str;
public:
Test(const string& s="Yangyang"):str(s){}
void operator()()
{
cout<<"Test::string="<< str<<endl;
}
};
void funa(Yang::ThreadPool& the)
{
Test te{};
for (int i = 0; i < 5;++i)
{
the.run(te);
}
}
int main()
{
Test te{};//该类重载小括号~为可执行对象
Yang::ThreadPool threadPool("Yang::ThreadPool");
threadPool.setMaxQueueSize(2);//2个事务
threadPool.setThreadCallback(print);//注册回调。每个线程启动后都要调用print
threadPool.start(10);//5个线程
//threadPool.run(std::bind(printString, "Yang"));//不能直接传printString给run()运行!!事务类型:void(void),绑定器可以解决!!
//threadPool.run(te);
//由于程序运行太快~导致后面的来不及执行->sleep()
//sleep(5);//unistd.h
std::thread tha(funa,std::ref(threadPool));
tha.join();
threadPool.stop();
return 0;
}
如有不足希望大家指出,谢谢支持!╰(*°▽°*)╯