C++ 线程池, 手写代码实现

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 个线程将会空转,一直等待任务的到来, 具体的一些运行策略 我们可以自己定义。
这牵扯到了,线程复用的技术。
复用: 线程池解决的是一个线程复用的问题,线程的启动成本是非常低的。
线程上下文的问题:通常很多资源会与上下文做绑定。

线程池的工作模式:

  1. 线程池启动时应该创建多个线程,做准备。
  2. 有任务来了,怎样分配任务?a. 分配,怎么分配。分配给是恶。通过 queue 来管理线程工作。 b. 没有任务的时候?线程停着 还是空转。condition_variable 根据条件等候 c. 有任务来了,怎么分配要让线程知道,这样才不会乱。 notify进行通知。
  3. 识别结果返回,并回调给客户。
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;  
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值