线程池的应用
代码结构
- 工作线程
- 代码如下
#ifndef WBTHE_THREAD_POOL_H_
#define WBTHE_THREAD_POOL_H_
#include <cassert>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <memory> // unique_ptr
#include <mutex>
#include <thread>
#include <list>
#include <vector>
class TaskQueue {
public:
TaskQueue() = default;
virtual ~TaskQueue() = default;
virtual void enqueue(std::function<void()> fn) = 0;
// 关闭
virtual void shutdown() = 0;
// 空闲状态
virtual void on_idle(){};
};
class ThreadPool : public TaskQueue {
public:
explicit ThreadPool(size_t n) : shutdown_(false) {
while (n) {
/**
* emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程
*/
threads_.emplace_back(worker(*this));
n--;
}
}
ThreadPool(const ThreadPool &) = delete;
~ThreadPool() override = default;
/**
* 传值方式下的std::function对象保存,使用转移操作std::move()
*/
void enqueue(std::function<void()> fn) override {
/**
* std::unique_lock 锁锁管理模板类,通过对mutex的封装,
* std::unique_lock对象以独占所有权的方式(unique owership)管理mutex对象的上锁和解锁操作,
* 即在unique_lock对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而unique_lock的生命周期结束之后,
* 它所管理的锁对象会被解锁。unique_lock具有lock_guard的所有功能,
* 而且更为灵活。虽然二者的对象都不能复制,但是unique_lock可以移动(movable),
* 因此用unique_lock管理互斥对象,可以作为函数的返回值,也可以放到STL的容器中。
* https://blog.csdn.net/fengbingchun/article/details/78638138
*/
std::unique_lock<std::mutex> lock(mutex_);
jobs_.push_back(std::move(fn));
cond_.notify_one();
}
void shutdown() override {
// Stop all worker threads...
{
std::unique_lock<std::mutex> lock(mutex_);
shutdown_ = true;
}
cond_.notify_all();
//: - auto 是类型占位符,使用时必须初始化,用作类型的推导,在编译期间替换为变量的实际类型
//: - auto 与 auto* 没有区别,但是auto声明引用类型时必须接&符号,int i = 10; auto &r = i;
//: - auto 在同一行声明多个变量时,所有的类型都相同, auto只推导第一个类型。后面的同第一个类型相同
//: - auto 不能做形参
// 不加“&”的意思是取出array里面的每个对象,
// 赋给t,t虽然被改变了,但数组里面的对象并没有变;加“&”表示t是数组里面 每个对象的别名
// Join...
for (auto &t : threads_) {
t.join();
}
}
private:
struct worker {
explicit worker(ThreadPool &pool) : pool_(pool) {}
void operator()() {
for (;;) {
std::function<void()> fn;
//======独占互斥体锁 begin======
{
std::unique_lock<std::mutex> lock(pool_.mutex_);
/**
[ ] 不捕获任何外部变量
[& ] 以引用方式捕获所有变量,可以修改,但要当心引用无效
*/
pool_.cond_.wait(
lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
fn = pool_.jobs_.front();
pool_.jobs_.pop_front();
}
//======end=====
/**
* 静态类型转换 转换为目标类型bool
*/
assert(true == static_cast<bool>(fn));
fn();
}
}
ThreadPool &pool_;
};
friend struct worker;
std::vector<std::thread> threads_;
std::list< std::function<void()> > jobs_;
bool shutdown_;
/**
用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,
直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。
*/
std::condition_variable cond_;
std::mutex mutex_;
};
#endif // WBTHE_THREAD_POOL_H_
网友评论