理解线程池源码 【C++】面试高频考点

news2025/9/18 23:15:18

理解线程池 C++

文章目录

  • 理解线程池 C++
    • 程序源码
    • 知识点
      • emplace_back 和 push_back有什么区别?
      • 互斥锁 mutex
      • condition_variable
      • std::move()函数
      • bind()函数
      • join 函数

线程池的原理就是管理一个任务队列和一个工作线程队列。

工作线程不断的从任务队列取任务,然后执行。如果没有任务就等待新任务的到来。添加新任务的时候先添加到任务队列,然后通知任意(条件变量notify_one/notify_all)一个线程有新的任务来了。

优势包括:

  • 资源管理:线程池有效地管理线程的创建、销毁和重用,避免了频繁创建和销毁线程的开销,节省了系统资源。

  • 减少线程创建时间:线程创建和销毁是开销较大的操作。线程池在初始化时创建一组线程,并将它们保持在就绪状态,从而在需要时可以快速执行任务,而不必每次都重新创建线程。

  • 任务队列:线程池通常与任务队列结合使用,任务可以被提交到队列中,线程池中的线程会按照队列中任务的顺序依次执行,确保了任务的有序执行。

  • 限制并发数:线程池可以限制并发执行的任务数量,以避免系统资源过度占用,提高系统稳定性。

  • 节省内存:线程池的线程可以被重复使用,避免了频繁创建线程的内存占用。

应用场景包括:

服务器应用:线程池在服务器应用中常用来处理客户端请求。当服务器需要处理大量的连接请求时,线程池可以有效地复用线程,提高服务器性能。

I/O密集型任务:线程池适用于I/O密集型任务,如文件读写、网络通信等。在这些情况下,线程可以在I/O操作等待时执行其他任务,提高了系统的效率。

并行计算:线程池可以用于并行计算,将任务拆分为多个子任务,由线程池中的线程并行执行,加速计算过程。

定时任务:线程池可用于执行定时任务,例如定期备份数据、清理日志等。

多任务并行处理:当需要处理多个任务,但不想为每个任务创建一个线程时,线程池是一种有效的方式。它可以控制并发任务的数量,从而避免系统过度负载。

20231012153035

程序源码


class ThreadPool {
 public:
  // 构造函数,传入线程数
  ThreadPool(size_t);
  template <class F, class... Args>
  auto enqueue(F&& f, Args&&... args)
      -> std::future<typename std::result_of<F(Args...)>::type>;
  ~ThreadPool();

 private:
  // 线程组
  std::vector<std::thread> workers;
  // 任务队列
  std::queue<std::function<void()> > tasks;

  // synchronization
  std::mutex queue_mutex; //互斥锁
  std::condition_variable condition;//条件变量
  bool stop;//停止标志
};

// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {
  for (size_t i = 0; i < threads; ++i)
    //thread 禁用了拷贝构造和赋值运算,因此这边最好用emplace_back
    workers.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();
      }
    });
}

// add new work item to the pool
template <class F, class... Args>
auto ThreadPool::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);

    // don't allow enqueueing after stopping the pool
    if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");

    tasks.emplace([task]() { (*task)(); });
  }
  condition.notify_one();
  return res;
}

// the destructor joins all threads
inline ThreadPool::~ThreadPool() {
  {
    std::unique_lock<std::mutex> lock(queue_mutex);
    stop = true;
  }
  condition.notify_all();
  for (std::thread& worker : workers) worker.join();
}

知识点

emplace_back 和 push_back有什么区别?

《c++ primer》书里说明:

1.当调用push或insert成员函数时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中。而当我们调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。emplace成员使用这些参数在容器管理的内存空间中直接构造元素。

2.其中对emplace_back的调用和第二个push_back调用都会创建新的Sales_data对象。在调用emplace_back时,会在容器管理的内存空间中直接创建对象。而调用push_back则会创建一个局部临时对象

3.标准库容器的emplace_back成员是一个可变参数成员模板,而push_back只能传入单个参数

20231010183634

总结就是emplace_back资源消耗更小,不会创建临时对象,同事可以进行多参数的传入,应用于像vector中T有多个参数的场景。

互斥锁 mutex

一种同步原语,用于多线程管理,确保临界资源只有一个线程访问,其中mtx.lock()表示上锁,mtx.unlock()表示解锁。上述线程池中为什么没有unlock呢?因 std::unique_lockstd::mutex 这个智能指针在退出{}代码块之后就会自动销毁。

condition_variable

condition_variable 条件变量是一种对象,能够阻止调用线程,直到收到通知才能恢复。一般配合 unique_lock 一起使用。当其中一个等待函数被调用时,线程保持阻塞状态,直到被另一个线程唤醒,该线程在同一condition_variable对象上调用通知函数。

//通过notify_one或者notify_all进行唤醒
void wait (unique_lock<mutex>& lck);
//只有pred返回false时才会阻塞,pred变为true时才能被唤醒
void wait (unique_lock<mutex>& lck, Predicate pred);

std::move()函数

左值变右值,可以参考C++ 左值右值以及std::move函数解释

bind()函数

每个参数可以绑定到一个值或者作为占位符:

  • 如果绑定到一个值,调用返回的函数对象将总是使用该值作为参数。
  • 如果作为占位符,调用返回的函数对象将转发传递给调用的参数(由占位符指定顺序号)
double my_divide (double x, double y) {return x/y;}

int main () {
  auto fn_five = std::bind (my_divide,10,2);               // returns 10/2
  std::cout << fn_five() << '\n';                          // 5

  auto fn_half = std::bind (my_divide,_1,2);               // returns x/2
  std::cout << fn_half(10) << '\n';                        // 5

  auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x
  std::cout << fn_invert(10,2) << '\n';                    // 0.2

  auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)
  std::cout << fn_rounding(10,3) << '\n';                  // 3
}

join 函数

C++中用于线程同步的一个方法,它通常用于等待一个线程的执行完成。当你创建一个线程并希望等待它完成执行。

int main() {
    std::thread t1(foo);
    std::thread t2(bar);

    t1.join();  // 主线程等待 t1 完成
    t2.join();  // 主线程等待 t2 完成

    std::cout << "Both threads have finished!" << std::endl;

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1086684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JDBC加载.properties文件的两种方式

maven项目 读resources下文件 也可以 也可以用ResouseBundle 对于ClassLoader这种方式,测试ok,但是打成war包部署到服务器,可能出现问题,什么原因不知道,以后来写

第十三更---程序员常用网站一览

今天聊点题外话。大家都在那里查找资料呢&#xff0c;如今的资源网站太多了&#xff0c;眼花缭乱。今天我把一些常见的平台罗列一下 大家还有什么宝藏网站的话多多评论区分享吧 目录 一.CSDN 二.掘金 三.菜鸟教程 四.MDN 五.牛客 六.博客园 七.b站 八.微信读书 一.CSD…

SNAP处理数据C盘越用越小,Datatype out of range报错

SNAP处理数据C盘越用越小&#xff0c;Datatype out of range报错 问题描述 SNAP处理的影像比较多了之后&#xff0c;占用C盘临时存储空间&#xff0c;在做处理时&#xff0c;一直报错Datatype out of range 原因 临时存储不够了&#xff0c;需要释放一下之前的空间。 解决…

网络-navigator.sendBeacon

文章目录 前言一、navigator.sendBeacon是什么&#xff1f;优点缺点 二、navigator.sendBeacon应用场景三、navigator.sendBeacon的使用四、用户停留时间埋点总结 前言 本文主要记录navigator.sendBeacon异步请求的使用&#xff0c;以及应用场景和埋点小demo。 一、navigator.…

Xray联动burp进行渗透测试

与 Burp 联动 - xray 安全评估工具文档 这是Xray的官方文档 Xray的被动扫描发送的流量比较小&#xff0c;Xray可以联动burp suite 使用&#xff0c;将burp抓到的包发给Xray&#xff0c;我们只需要配置好代理一直点就行&#xff0c;然后查看渗透测试报告 xray_windows_amd64.e…

YOLOv5算法改进(3)— 注意力机制介绍(ECA、SOCA和SimAM)

前言:Hello大家好,我是小哥谈。注意力机制是近年来深度学习领域内的研究热点,可以帮助模型更好地关注重要的特征,从而提高模型的性能。注意力机制可被应用于模型的不同层级,以便更好地捕捉图像中的细节和特征,这种模型在计算资源有限的情况下,可以实现更好的性能和效率。…

vulnhub_Fowsniff靶机渗透测试

Fowsniff靶机 靶机地址&#xff1a;https://www.vulnhub.com/entry/fowsniff-1,262/ 文章目录 Fowsniff靶机信息收集web渗透密码碰撞POP3邮件服务器渗透获取权限权限提升靶机总结 信息收集 通过nmap扫描&#xff0c;靶机开放22 80 110 143端口&#xff0c;110是pop3邮件服务…

京东运营数据分析:2023年8月京东宠物主粮行业品牌销售排行榜

鲸参谋监测的京东平台8月份宠物主粮市场销售数据已出炉&#xff01; 随着养宠人群的逐渐增多&#xff0c;宠物经济规模也进一步庞大。宠物生活市场中&#xff0c;宠物主粮作为养宠人群的刚需品&#xff0c;其市场规模也在进一步扩大。鲸参谋数据显示&#xff0c;今年8月份&…

如何将中文翻译成日语:文件批量重命名的有效方法

随着全球化的发展&#xff0c;多语言交流变得越来越重要。在许多情况下&#xff0c;我们需要将中文文件翻译成日语&#xff0c;以便更好地进行国际交流。在这个过程中&#xff0c;文件重命名是一种非常有效的技巧&#xff0c;可以帮助我们更快、更准确地完成翻译任务。本文将介…

南美乌拉圭市场最全分析开发攻略,收藏一篇就够了

乌拉圭国家虽小&#xff0c;但是消费能力是不低的&#xff0c;也是南美南美最富的国家之一。中国是乌拉圭第一大贸易伙伴&#xff0c;乌拉圭公民对中国及中国的商品好感度较高&#xff0c;2022年初&#xff0c;中国-乌拉圭海关AEO互认&#xff0c;为中乌经贸合作发展注入了新动…

【Spring AOP】统一用户登录校验

统一用户登录校验 一. 使用拦截器实现统一用户登录校验1. 自定义拦截器2. 将拦截器加入到系统配置 二. 拦截器实现原理三. 扩展&#xff1a;统一访问前缀添加 一. 使用拦截器实现统一用户登录校验 Spring 中提供了具体的实现拦截器&#xff1a;HandlerInterceptor&#xff0c;…

【计算机系统】校验码

【计算机系统】校验码 校验码奇偶校验码海明码校验位的位数校验位的位置确定校验的值校验错误检测 循环冗余校验码 校验码 计算机系统运行时&#xff0c;为了确保数据在传送过程中正确无误&#xff0c;一是提高硬件电路的可靠性&#xff0c;二是提高代码的校验能力。通常使用校…

使用telnet+nc工具测试网络连通性

背景&#xff1a; 正常情况下使用ping命令即可测试网络的连通性&#xff0c;但如果做了内网穿透(端口转发)&#xff0c;则需要指定网络端口&#xff0c;此时ping命令无法实现ipport的连通性测试。则可以使用telnetnc测试网络连通性。 环境&#xff1a; 两台服务器都是按照的De…

【Spring AOP】统一异常处理

统一异常处理 统⼀异常处理使⽤的是 ControllerAdvice ExceptionHandler 来实现的&#xff0c; 类上面加上 ControllerAdvice 注解表示控制器通知类方法上面加上 ExceptionHandler 表示异常处理器&#xff0c;并添加异常返回的业务代码 两个结合表示当出现异常的时候执⾏某个…

一文带你简单了解一下堡垒机是干嘛的!

随着国家对网络安全的重视&#xff0c;随着等保政策的落地执行&#xff0c;越来越多的企业知道了堡垒机。但对于堡垒机的作用还不是很了解&#xff0c;很多人在问&#xff0c;堡垒机是干嘛的、这里我们小编就跟大家来简单唠唠。 首先我们来看看什么是堡垒机&#xff1f; 堡垒…

VBA入门3——过程和函数 (Sub | Function)

VBA基础入门2 VBA 过程和函数 (Sub | Function)VBA 过程(Sub) 入门教程和实例&#xff08;组织代码的容器&#xff09;无参数过程有参数过程调用子过程&#xff08;Sub&#xff09;调用子过程和函数的基本语法提前退出过程 VBA 函数(Function)入门教程和实例&#xff08;重复使…

Cookies Session JWT

Cookies Cookies是会话跟踪技术的一种&#xff0c;通过将数据保存到客户端的浏览器上以此来达到会话间共享数据的效果。 从打开浏览器开始访问页面直到关闭浏览器断开连接算作一次会话&#xff08;Session&#xff09;&#xff0c;一次会话中可以包含多次请求-响应。每次的请…

【linux】权限相关问题

【linux】权限相关问题 一.用户的分类sudo 二.文件执行的权限i. 文件的分类ii.人的分类三.修改创建文件的权限chmod更改文件创造的默认权限(umask) 三.删除&#xff08;粘滞位&#xff09; 一.用户的分类 在我们使用linux的时候&#xff0c;有用户类型的区分&#xff0c;不同用…

Linux环境下Qt应用程序打包与发布

本文介绍Linux环境下Qt应用程序的打包与发布。 Linux环境下&#xff0c;在开发机器上开发完应用程序&#xff0c;需要部署到其他非开发环境的机器上&#xff0c;这时&#xff0c;需要对开发的Qt应用程序进行打包&#xff0c;以确保可以在其他机器平台&#xff08;非开发环境&a…

20231012_python练习_服务端与客户端数据交互v2_增加xlsx表格数据批量导入数据库

服务端增加根据上传附件格式 xlsx 类型&#xff0c;将表格第一个sheet数据批量快速导入数据库 服务端 import socketserver import json import os #import pymysql import cx_Oracle #Oracle 数据库连接 import time import tqdm import pandas as pd import openpyxlclass …