C++11实现线程库
本篇博客通过packaged_task 封装一个可以让用户拿到线程结果的线程池。
packaged_task 可以封装模板类,可以封装一个函数,模板类型是该函数的返回值类型,可以通过get_future 获得future来获得函数的返回值。该线程池最终的一个函数就是想线程池中push任务的函数,用户可以自己要执行的函数以及参数传递进来,然后线城池启动线程进行执行,通过函数返回值的方式让用户拿到数据。因为用户要执行的参数个数类型是不确定的,所以该函数是一个模板函数,第一个模板参数是要执行的函数,第二个是可变参数模板,用来接收用户的参数,在函数内部通过绑定参数,来得到一个无参的可执行对象,通过构造lambda的方式来添加进入任务队列。具体代码如下:
#pragma once
#include <iostream>
#include <future>
#include <vector>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <memory>
class ThreadPool
{
using Functor = std::function<void()>;
public:
ThreadPool(int threadnum = 5) : _stop(false)
{
for(int i = 0; i < threadnum; ++i)
{
_threads.emplace_back(std::thread(&ThreadPool::entry, this));
}
}
// 对任务进行封装packaged_task,使用lambda传入任务池
template<typename F, typename ...Args>
auto push(F&& f, Args&& ...args) -> std::future<decltype(f(args...))>
{
// 获取返回值类型
using return_type = decltype(f(args...));
// 把参数进行绑定
auto fun = std::bind(&std::forward<F>(f), std::forward<Args>(args)...);
// 封装成为packaged_task 指针, 一定要构建堆上的对象
auto pack_task_ptr = std::make_shared<std::packaged_task<return_type()>>(fun);
// 获取返回值
std::future<return_type> res = pack_task_ptr->get_future();
// 加锁
{
std::unique_lock<std::mutex> lock(_mutex);
// 封装成为lambda 加入到任务池
_funcs.emplace_back([pack_task_ptr](){ (*pack_task_ptr)(); });
}
// 唤醒所有线程
_con.notify_all();
return res;
}
void stop()
{
_stop = true;
_con.notify_all();
for(auto& th : _threads)
{
th.join();
}
}
~ThreadPool()
{
stop();
}
private:
// 线程的入口函数
void entry()
{
while(!_stop)
{
// 临时变量,一次拿到所有的任务,一下全部执行,避免频繁的加锁解锁
std::vector<Functor> tmp;
{
std::unique_lock<std::mutex> lock(_mutex);
// while(!_stop && _funcs.empty())_con.wait(lock);
_con.wait(lock, [this](){ return _stop || !_funcs.empty();});
tmp.swap(_funcs);
}
for(auto task : tmp)
{
task();
}
}
}
private:
std::atomic<bool> _stop; // 表示线城池是否停止了
std::mutex _mutex; //互斥锁
std::condition_variable _con; // 条件变量
std::vector<Functor> _funcs; // 任务池
std::vector<std::thread> _threads; // 管理的线程
};