仿RabbitMQ实现消息队列(五)--C++11异步操作实现线程池
·
future
介绍
std::future 是 C++11 标准库中的一个模板类,它表示一个异步操作的结果。当我们在
多线程编程中使用异步任务时,std::future 可以帮助我们在需要的时候获取任务的执行
结果。std::future 的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保
我们在获取结果时不会遇到未完成的操作
应用场景
- 异步任务:
- 并发控制:
- 结果获取:
用法示例
- std::async关联异步任务
async是一种将任务与future关联的简单方法。它创建并运行一个异步任务。并返回一个与该任务结果关联的future对象。默认情况下,async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为launch类型:
线程池
基于线程池执行任务的时候,入口函数内部执行逻辑是固定的,因此选择packaged_task加上future的组合来实现。
工作思想:
-
用户传入要执行的函数,以及需要处理的数据,由线程池中的工作线程来执行函数来完成任务
实现: -
管理的成员
任务池:
互斥锁:
一定数量的工作线程: -
管理的操作
入队任务
停止运行
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <vector>
using namespace std;
class ThreadPool
{
public:
using Functor = function<void(void)>;
ThreadPool(int count = 1) : _stop(false)
{
for (int i = 0; i < count; i++)
{
_threads.emplace_back(&ThreadPool::entry, this);
}
}
void stop()
{
if (_stop)
return;
_stop = true;
_cv.notify_all(); // 唤醒所有工作线程
for (auto &threads : _threads)
{
threads.join();
}
}
// push传入的首先是一个函数--用户要执行的函数,接下来是不定参,表示要处理的数据也就是函数中的参数
// push内部,会将这个传入的函数封装成一个异步任务(packaged_task),抛入到任务池中,由工作线程取出进行执行
// lambda生成一个可调用对象(内部执行异步任务),抛入到任务池中,由工作线程取出进行执行
template <typename F, typename... Args> // 不定参
auto push(F &&func, Args &&...args) -> future<decltype(func(args...))>
{
// 将传入的函数封装成packaged_task任务
using return_type = decltype(func(args...));
//由于不确定参数 先绑定函数
auto Func = std::bind(forward<F>(func), forward<Args>(args)...);
auto task = make_shared<packaged_task<return_type()>>(Func);
future<return_type> fu = task->get_future();
// 构造一个lambda匿名函数(捕获任务对象),函数内执行任务对象
{
unique_lock<mutex> lk(_mutex); // 加锁
// 将构造出来的匿名对象,抛入到任务池中
_taskpool.push_back([task](){
(*task)(); });
_cv.notify_all();
}
return fu;
}
~ThreadPool()
{
stop();
}
private:
// 线程入口函数--内部不断地从任务池中取出任务进行执行
void entry()
{
vector<Functor> task_pool;
{
// 加锁
unique_lock<mutex> lock(_mutex);
// stop被置位返回 去出任务进行执行
_cv.wait(lock, [this]()
{ return _stop || _taskpool.empty(); });
// 取出任务执行 一次性全部交换出来
task_pool.swap(_taskpool);
}
for (auto &task : task_pool)
{
task();
}
}
private:
mutex _mutex;
vector<Functor> _taskpool; // 任务池
vector<thread> _threads;
condition_variable _cv;//条件变量
atomic<bool> _stop; // 原子类型数
};
_taskpool.push_back(task{ (*task)(); })。 里面是匿名函数,捕获了task,保存在lambda所生成的可调用对象里。将生成的可调用对象Push到任务池中。
注意上面的这个函数参 auto push(F &&func, Args &&…args) -> future<decltype(func(args…))> F &&func类型是个万能引用,这里如果加上一个const (const F&& func),那这个const F&& 就是一个带const 的右值引用 就只能接收右值对象,我们在后面调用bind函数的时候 参数结果是左值,push会出现错误。解决方法:可以用move变成右值,F&& func 就是个万能引用。去掉const后,func可以被正常绑定,修改(或修改)避免了额外的类型限制。
更多推荐
所有评论(0)