线程池代码及知识点
#include<thread>
#include<condition_variable>
#include<mutex>
#include<vector>
#include<queue>
#include<future>
class ThreadPool{
private:
bool m_stop; //线程池是否停止
std::vector<std::thread>m_thread; //线程池
std::queue<std::function<void()>>tasks;//任务队列
std::mutex m_mutex; //互斥锁实现线程同步
std::condition_variable m_cv; //线程环境锁,可以让线程处于休眠或者唤醒状态
public:
explicit ThreadPool(size_t threadNumber):m_stop(false){
for(size_t i=0;i<threadNumber;++i)
{
m_thread.emplace_back( //该函数是C++11新增加的,其功能和push_back()相同,都是在vector容器的尾部添加一个元素。
[this](){
for(;;)
{
std::function<void()>task;
{
std::unique_lock<std::mutex>lk(m_mutex);// unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock()
m_cv.wait(lk,[this](){ return m_stop||!tasks.empty();});// wait 直到需要停止或有 task
if(m_stop&&tasks.empty()) return;
task=std::move(tasks.front());//按先进先出从队列取一个task
tasks.pop();
}
task();//执行任务
}
}
);
}
}
ThreadPool(const ThreadPool &) = delete;
ThreadPool(ThreadPool &&) = delete;
ThreadPool & operator=(const ThreadPool &) = delete;
ThreadPool & operator=(ThreadPool &&) = delete;
~ThreadPool(){
{
std::unique_lock<std::mutex>lk(m_mutex);
m_stop=true;
}
m_cv.notify_all();
for(auto& threads:m_thread)
{
threads.join();
}
}
template<typename F,typename... Args>
auto submit(F&& f,Args&&... args)->std::future<decltype(f(args...))>{
/*以 args-func 为 <T> 的构造函数参数列表,构造 T 类型对象并将它包装于 std::shared_ptr,
*T是类模板 std::packaged_task 包装的可调用 (Callable) 目标函数,即std::bind绑定的f(arg...)
*可移动的共享互斥体所有权包装,封装到共享指针中,支持复制构造,并隐藏了函数参数集*/
auto taskPtr=std::make_shared<std::packaged_task<decltype(f(args...))()>>(
// 采用std::bind绑定函数f及形参集args...创建函数对象(decltype获取函数对象类型)
std::bind(std::forward<F>(f),std::forward<Args>(args)...)
);
{
std::unique_lock<std::mutex>lk(m_mutex);
if(m_stop) throw std::runtime_error("submit on stopped ThreadPool");
tasks.emplace([taskPtr](){ (*taskPtr)(); });
}
m_cv.notify_one();
return taskPtr->get_future();
}
};
链接: https://zhuanlan.zhihu.com/p/61464921
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
线程池
线程池是将若干个随时可以执行任务的线程放在“池子”这种容器中,当我们要使用线程的时候,从线程池中取出即可;使用完成后再将线程归还给线程池,以便下次或者其他用户使用。使用线程池技术的最大好处是服务器可以避免因重复的建立和销毁线程带来的开销,从而提高服务器对客户端的响应速度。
如何实现一个简易的线程池
- 首先,我们使用一个队列来存储任务序列。这一部分的核心就是怎么实现将一个任务提交到任务序列,也就是入队。
一个任务,一般可以表示为一个函数。那么我们需要传入的参数就有一个函数指针以及这个函数的所有参数。其中一个问题是这个函数的参数往往不是不确定的,类型也是未知的,因此,我们需要使用可变参数函数模板。二者,里面的一个难点在于怎么将各种不同的类型统一。我们使用 std::bind+std::funtion+std::packaged_task 等层层封装。最后就是同步和互斥操作。
- 接着,就是线程池的工作过程。我们用一个 vector 来存储线程。主要的思路就是在没任务的时候保持线程处于非活跃状态,有任务的时候进行处理。其中也利用了 lambda 表达式的特性,以及同步和互斥操作。
- 最后,就是线程池的析构实现。用一个 bool 变量来存储是否要停止使用线程池,当需要析构的时候,唤醒所有线程,并且逐个 join。
分类:
狂学webserver
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析