cpp11实现线程池(一)——项目介绍

12 篇文章 1 订阅
10 篇文章 0 订阅

项目介绍

线程池是库的形式提供给用户,是必须放到代码中,不能单独运行,亦称为基础组件

第一版线程池任务对象使用继承技术,提供一个抽象基类Task,里面有一个纯虚函数run(),使用时继承该类,并重写该纯虚函数,在这个重写的纯虚函数中完成要执行的具体任务;

第二版线程池使用C++11 的std::packaged_task<T>来包装任务执行函数,用std::future<T>来接受任意类型的结果

完整源代码

https://github.com/StrikeCode/cpp11_threadpool.git

技术点

熟练基于C++ 11标准的面向对象编程

组合和继承、继承多态、STL容器、智能指针、函数对象、绑定器、可变参模板编程等。

熟悉C++11多线程编程

threadmutexatomiccondition_variableunique_lock等。

C++17和C++20标准的内容

C++17的any类型和C++20的信号量semaphore,项目上都我们自己用代码实现。

熟悉多线程理论

多线程基本知识、线程互斥、线程同步、原子操作、CAS等。

线程池优点

服务进程启动之初,就实现创建好线程池,当业务流量到来时,需要线程就可以直接从线程池获取线程执行业务处理,不需要来一个任务创建一个线程,执行完任务再销毁线程,减少了线程的创建与销毁的开销

fixed线程池模式

线程池里面的线程个数是固定不变的,一般是ThreadPool创建时根据当前机器的CPU核心数量进行指定。

cached模式线程池

线程池里面的线程个数是可动态增长的,根据任务的数量动态的增加线程的数量,但是会设置一个线程数量的阈值(线程过多的坏处上面已经讲过了),任务处理完成,如果动态增长的线程空闲了60s还没有处理其它任务,那么关闭线程,保持池中最初数量的线程即可

多线程优势

多线程程序就一定好吗?不一定,需要具体分析场景

IO密集型

无论是CPU单核或者CPU多核还是多CPU,都是适合多线程(因为IO多就会造成CPU空闲)

CPU密集型

CPU单核情况下,不适合多线程,因为多线程存在上下文切换的开销,不如单线程直接执行完成,因为CPU密集型场景也不怎么到等待状态

线程池的开销

为了完成任务,创建很多的线程可以吗?

  • 线程的创建和销毁都是非常"重"的操作, 需要创建相应的数据结构如PCB task_struct,需要描述地址空间相应的数据结构 vm_struct、vm_area_struct,需要创建相应的页目录和页表项;
  • 线程栈(线程执行函数会使用到)本身占用大量内存,32位机一般一个进程最多只能创建380个线程左右,因为:
  • 假设一个线程需要8M内存空间,一个进程的虚拟内存用户空间为3G,3G/8M = 384;
  • 线程的上下文切换要占用大量时间;
  • 大量线程同时唤醒会使系统经常出现锯齿状负载或者瞬间负载量很大导致宕机,比如同一时间很多IO操作的已经完成

线程同步

竞态条件

指设备或系统临界区代码段出现不恰当的执行时序,而得到不正确的结果

如果多线程环境下不存在竞态条件,则称为可重入(多线程)的代码段,反之称为不可重入的代码段

线程互斥

  • 互斥锁

  • atomic原子类型,一般利用CAS操作(无锁机制)实现,常见应用:无锁队列、链表、数组(使用活锁)

线程通信

条件变量 condition_variable

使用方法:mutex+condition_variable

waitsignal的交互状态关系:

在这里插入图片描述

问题:用notify_one还是notify_all? notify_one可能会死锁

  • 信号量 semaphore

可以用条件变量封装一个信号量

信号量可看作资源计数没有限制的互斥量:

mutex.lock() //锁的资源计数1 -》0

critical section

mutex.unlock()//锁的资源计数0 -》1

控制的精度不如条件变量

用于保证操作时序,而没有对资源的互斥访问

使用wait (-1)和 post(+1) 方法

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以,下面是一个基于C++11的线程池示例: ```cpp #include <iostream> #include <vector> #include <queue> #include <thread> #include <mutex> #include <condition_variable> #include <functional> class ThreadPool { public: ThreadPool(size_t numThreads) : stop(false) { for (size_t i = 0; i < numThreads; i++) { threads.emplace_back([this] { for (;;) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) { return; } task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread& thread : threads) { thread.join(); } } template<class F, class... Args> auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> { using return_type = typename std::result_of<F(Args...)>::type; auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)); std::future<return_type> res = task->get_future(); { std::unique_lock<std::mutex> lock(queue_mutex); if (stop) { throw std::runtime_error("enqueue on stopped ThreadPool"); } tasks.emplace([task] { (*task)(); }); } condition.notify_one(); return res; } private: std::vector<std::thread> threads; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop; }; ``` 在这个线程池中,我们使用了C++11的新特性`std::thread`、`std::mutex`、`std::condition_variable`等来实现线程池的基本功能。同时,我们也使用了C++11的新特性`std::future`和`std::packaged_task`来实现任务的异步执行和结果的获取。 使用时,可以通过以下方式来创建一个线程池,并提交任务: ```cpp ThreadPool pool(4); auto result = pool.enqueue([](int answer) { return answer; }, 42); std::cout << "result: " << result.get() << std::endl; ``` 这个例子中,我们创建了一个拥有4个线程的线程池,并提交了一个lambda表达式作为任务,lambda表达式接受一个int类型的参数,返回一个int类型的结果。我们通过`result.get()`来获取任务的执行结果。 当然,在实际使用中,我们可以通过`enqueue`函数提交不同类型的任务,只要任务符合函数的参数和返回值类型即可。同时,我们也可以通过控制线程池线程的数量来更好地控制程序的性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值