C++ 线程池
这是本人第一次写博客,最近学习C++ 一段时间了,写一些自己学到的知识。所以选择了 C++ 线程池
// 首先导包,本教程都会使用
#include <iostream>
#include <thread>
#include <string>
#include <future>
#include <mutex>
#include <atomic>
#include <functional> // 存的是排序啊, max_element,bind等函数
#include <vecctor>
#include <queue>.
我先介绍一些会用到的关键字:
- promise :感性的理解给出一个承诺我会在线程结束返回一个值。
- future: 用来得到 promise 承诺的未来的值。
void m0(promise<string>& pro){
// 线程 sleep 1s
this_thread::sleep_for(chrono::milliseconds(1000));
printf("m0 执行结束!\n");
pro.set_value("这是我承诺给出的值,你现在已经在未来得到了!\n");
}
int main(){
std::promise<string> pro;
// 如果线程里边传的参数是引用,就要std::ref一下
// m0 是我们的作用对象函数,
thread t0(m0, std::ref(pro));
shared_future<string> fut = pro.get_future();
// 注意此时: fut 是不可以复制的,因为她的实现类内部使用了如下代码:
// future& operator=(const future&) = delete;
if(t0.joinable())
t0.join();
printf("t0 执行的结果为: \n%s\n", fut.get().c_str());
return 0;
}
预备工作结束,正式进入线程池的世界:
背景:
在计算机程序中,有的时候我们会面临一些重复的任务,这些任务除了参数不同外,实现功能都相同。那么我是否可以通过多线程的技术来提高运行效率呢?
当然可以。但是面临两个问题:
- 线程的创建和销毁是一个很重的操作,简言之,耗时间,耗资源。
- 我不能没有限制创建线程。因为你的计算机总要 同时做一些其他的工作的,如果开启线程过多,轻则系统卡顿,重则线程死锁。
为了解决以上两个问题,计算机行业的前辈们提出了线程池的概念。
线程池,它是一种模式。类似的还有内存池,连接池。其实也就是我们自己维护一个队列,队列容量可以自己定义,也可以运行时指定容量。队列里面放的是我们的已经启动的线程。
假如,队列里面放了 5 个线程, 我现在有 7 个任务, 那我就会让其中 5个线程 先去做 5 个任务, 等它们之中有谁 空闲后,去执行剩下的2个任务。如果, 我没有通知队列停止运行,它们这 5 个线程将会空转,一直等待任务的到来, 具体的一些运行策略 我们可以自己定义。
这牵扯到了,线程复用的技术。
复用: 线程池解决的是一个线程复用的问题,线程的启动成本是非常低的。
线程上下文的问题:通常很多资源会与上下文做绑定。
线程池的工作模式:
- 线程池启动时应该创建多个线程,做准备。
- 有任务来了,怎样分配任务?a. 分配,怎么分配。分配给是恶。通过 queue 来管理线程工作。 b. 没有任务的时候?线程停着 还是空转。condition_variable 根据条件等候 c. 有任务来了,怎么分配要让线程知道,这样才不会乱。 notify进行通知。
- 识别结果返回,并回调给客户。
using namespace std;
// 定义一个任务需要 使用到的参数。
struct Job{
shared_ptr<promise<int>> promise_;
string arg_str_;
int a_;
int b_;
};
class ThreadPool{
public:
bool start(int num_threads){
stop();
keep_run_ = true;
vector<shared_ptr<promise<bool>>> pros;
for(int i=0; i <= num_threads; ++i){
shared_ptr<promise<bool>> pro = make_shared<promise<bool>>();
threads_.push_back(make_shared<thread>(&ThreadPool::worker, this, i, pro));
/*
vector<stu_info> v; // stu_info 是一个类
v.push_back(stu_info("nginx")); // 先构造再拷贝
emplace 直接在列表内部继续构造,不需要再次拷贝了
vector<stu_info> v;
v.emplace_back("redis");
*/
pros.emplace_back(pro); // 更快的push_back, 不用再去拷贝
}
bool all_is_true = true;
for(auto& pro : pros)
all_is_true = all_is_true && pro->get_future().get();
if(!all_is_true)
stop();
return all_is_true;
}
void stop(){
if (!keep_run_) return;
keep_run_ = false;
cv_.notify_all();
for(auto& t : threads_){
if (t && t -> joinable()) // 如果此时 条件判断 为 False 呢?
t->join();
}
threads_.clear();
}
shared_future<int> commit(const string& arg_str, int a, int b){
unique_lock<mutex> l(jobs_lock_);
Job job;
job.promise_ = make_shared<promise<int>>();
job.arg_str_ = arg_str;
job.a_ = a;
job.b_ = b;
jobs_.push(job);
cv_.notify_one();
return job.promise_ -> get_future();
}
private:
void worker(int thread_id, shared_ptr<promise<bool>> pro){
pro -> set_value(true);
Job job;
// 如果处于运行状态的话
while(keep_run_){
unique_lock<mutex> l(jobs_lock_);
{
// 如果此时, 线程队列是空的 或者 正在运行状态,则进行等待
cv_.wait(l, [&]{return !jobs_.empty() || !keep_run_;});
if(!keep_run_)
break;
// 取最前面的元素
job = jobs_.front();
// 删除掉最前面的元素
jobs_.pop();
}
int result = job.a_ * job.b_;
printf("正在执行的线程ID: %d, 参数: %d, %d, 线程名字: %s\n",
thread_id, job.a_, job.b_, job.arg_str_.c_str());
this_thread::sleep_for(chrono::microseconds(1000));
job.promise_ -> set_value( result);
}
// 退出打印 ID
printf("Thread exit %d\n", thread_id);
}
private:
vector<shared_ptr<thread>> threads_;
atomic<bool> keep_run_{false};
queue<Job> jobs_;
mutex jobs_lock_;
condition_variable cv_;
};
int main(){
ThreadPool threadpool;
if(!threadpool.start(3)){
printf("Start Failure! Please Check.\n");
return 0;
}
auto f1 = threadpool.commit("job1", 12, 14);
auto f2 = threadpool.commit("job2", 11, 14);
auto f3 = threadpool.commit("job3", 10, 14);
auto f4 = threadpool.commit("job4", 9, 14);
auto f5 = threadpool.commit("job5", 8, 14);
auto f6 = threadpool.commit("job6", 7, 14);
auto f7 = threadpool.commit("job7", 6, 14);
printf("f1 = %d, f2 = %d, f3 = %d, f4 = %d, f5 = %d, f6 = %d, f7 = %d", f1.get(),
f2.get(), f3.get(), f4.get(), f5.get(), f6.get(), f7.get()
);
getchar();
threadpool.stop();
return 0;
}