当前位置: 首页 > news >正文

c++并发编程面试题

1. C++中lock_guard和unique_lock的区别?

在C++中,lock_guard和unique_lock都是用于管理互斥锁的类,它们提供了一种 RAII(Resource Acquisition Is Initialization)机制来确保锁在作用域结束时自动释放。尽管它们的目的相似,但在功能和适用场景上有一些区别:

  1. 功能上的区别

lock_guard:

  • 主要用于一个简单的锁管理场景。
  • 在构造时自动获取锁,并在析构时自动释放锁。
  • 不支持手动解锁或重新锁定功能,锁的获取和释放是严格绑定到对象的生命周期。

unique_lock:

  • 提供更丰富的功能。
  • 可以在构造时指定是否立即锁定,可以在作用域内手动解锁和重新锁定。
  • 支持状态查询(例如,检测锁是否被持有)和条件变量的等待功能(通过与条件变量配合使用)。
  • 允许移动,但不可复制。
  1. 性能
  • lock_guard相对更轻量,适合简单的加锁需求。
  • unique_lock因为功能更丰富,可能在某些情况下性能稍逊,但提供了更多灵活性。
  1. 使用场景

使用 lock_guard:

  • 当你只需要简单的锁保护,且生命周期管理是清晰的情况下,使用 lock_guard 是最佳选择。

使用 unique_lock:

  • 当你需要更复杂的锁逻辑,例如需要在某个条件下释放锁,或者需要与条件变量一起使用时,选择 unique_lock 更为合适。

示例代码

#include <iostream>  
#include <mutex>  
#include <thread>  std::mutex mtx;  void function_with_lock_guard() {  std::lock_guard<std::mutex> lock(mtx);  // 保护的代码块  std::cout << "Lock guard is holding the lock." << std::endl;  
}  void function_with_unique_lock() {  std::unique_lock<std::mutex> lock(mtx);  // 保护的代码块  std::cout << "Unique lock is holding the lock." << std::endl;  // 这里可以手动释放锁  lock.unlock();  std::cout << "Lock has been released." << std::endl;  // 重新锁定  lock.lock();  std::cout << "Lock is locked again." << std::endl;  
}  int main() {  std::thread t1(function_with_lock_guard);  std::thread t2(function_with_unique_lock);  t1.join();  t2.join();  return 0;  
}  

总结

在大多数情况下,如果只需要简单的锁定和解锁,建议使用 lock_guard。如果需要更复杂的锁操作或条件变量的支持,选择 unique_lock。

2. C++中thread的join和detach的区别?

在C++中,std::thread提供了两种主要的线程管理方法:join和detach。它们在处理线程的生命周期和回收资源方面有重要的区别。

  1. join
  • 功能: 当你调用join方法时,主线程会等待被调用的线程完成执行。
  • 阻塞行为: 调用join会阻塞调用线程,直到目标线程结束。
  • 资源管理: 一旦调用了join,相关的线程资源会被正确地清理,确保没有资源泄漏。
  • 使用场景: 通常在你需要确保一个线程完成其工作后再继续执行主线程的情况下使用。例如,当线程完成了一项重要的计算或任务,并且主线程需要该结果时。
#include <iostream>  
#include <thread>  void threadFunction() {  std::cout << "Thread is running." << std::endl;  
}  int main() {  std::thread t(threadFunction);  t.join();  // 等待线程t执行完毕  std::cout << "Thread has finished." << std::endl;  return 0;  
}  
  1. detach
  • 功能: 当你调用detach时,被调用的线程会独立于创建它的线程继续执行。
  • 非阻塞行为: 调用detach后,主线程不会阻塞,线程会在后台运行。
  • 资源管理: 一旦线程被分离,主线程无法再与之交互,也不能调用join。系统会自动管理这个线程的资源,因此可能会出现资源泄漏或未定义的行为,特别是如果主线程在后台线程完成前就退出。
  • 使用场景: 通常在你希望后台线程独立执行一些任务,同时不需要等待它完成的情况下使用。例如,处理日志记录或异步计算。
#include <iostream>  
#include <thread>  void threadFunction() {  std::cout << "Thread is running." << std::endl;  
}  int main() {  std::thread t(threadFunction);  t.detach();  // 将线程t分离,不等待其结束  std::cout << "Thread has been detached." << std::endl;  // 给线程一些时间完成任务(仅用于示范,实际应用中应使用更安全的方法)  std::this_thread::sleep_for(std::chrono::seconds(1));  return 0;  
}  

总结

  • join: 等待一个线程完成执行,适用于需要同步的场合。
  • detach: 使线程独立于主线程执行,适用于后台处理或不需要同步的场合。

在使用这些方法时,确保理解线程的生命周期,以避免潜在的资源泄漏或未定义行为。

3. C++中jthread和thread的区别?

在C++20中,引入了std::jthread,这是对std::thread的一个改进。以下是std::jthread和std::thread之间的主要区别:

  1. 自动管理线程生命周期

std::jthread:

  • 自动管理线程的生命周期,使用了RAII(资源获取即初始化)原则。
  • 如果std::jthread对象离开作用域,它会自动调用join(),确保线程在对象析构时被正确地加入(join),避免了遗留的线程。

std::thread:

  • 需要手动管理线程的生命周期,调用join()或detach(),如果不处理,将导致程序异常终止。
  1. 更简便的使用

std::jthread:

  • 通过构造函数直接接收可调用对象和参数,并在析构时自动进行join,使用起来更为方便。

std::thread:

需要在确定线程应当结束时手动调用join(),增加了出错的可能性。

  1. 支持可中断的线程

std::jthread:

  • 提供了request_shutdown()方法来请求线程的终止。线程可以在合适的位置检查这个请求,从而安全地退出。

std::thread:

  • 没有内置的中断机制,终止线程需要使用其他方法(如设置标志位、使用条件变量等),不够灵活。
  1. 使用方法示例
#include <iostream>  
#include <thread>  void threadFunction(std::stop_token stoken) {  while (!stoken.stop_requested()) {  // 执行一些任务  std::this_thread::sleep_for(std::chrono::milliseconds(100));  }  std::cout << "Thread exiting." << std::endl;  
}  int main() {  std::jthread jt(threadFunction);  // 自动管理线程的生命周期  // 模拟主线程工作  std::this_thread::sleep_for(std::chrono::seconds(1));  // 请求线程结束  jt.request_stop();   return 0; // 自动调用 jt.join(),确保线程结束  
}  

总结

std::jthread 添加了自动管理和中断机制,使得线程管理更为安全和便捷。
std::thread 需要手动管理线程的生命周期,对用户的责任要求更高。
在大多数情况下,如果在C++20及以上版本中使用线程,推荐使用 std::jthread 以避免常见的线程管理问题。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • uniapp获取swiper中子组件的内容高度
  • ffmpeg 命令图片和视频转换
  • 【资源】wordpress 子比主题
  • 【数据结构和算法】(基础篇二)——链表
  • Centos 7.9 安装 图解版 小白必看 最新
  • Vue3+Element-plus+setup使用vuemap/vue-amap实现高德地图API相关操作
  • 嵌入式初学-C语言-二一
  • Java面试八股之什么是MQTT协议
  • 关于springboot的拦截器能力源码分析
  • URLSession之初窥门径
  • 智能家电入驻亚马逊VC有什么优势?为什么众多国内厂家都选择亚马逊VC?——WAYLI威利跨境助力商家
  • 实战 Springboot2 集成Redis 哨兵模式、集群模式、缓存管理、Lettuce拓扑刷新
  • 【Oracle点滴积累】解决ORA-20001: Latest xml inventory is not loaded into table故障的方法
  • 麻雀搜索算法(SSA)与支持向量机(SVM)结合的预测模型(SSA-SVM)及其Python和MATLAB实现
  • 指针(下)
  • 收藏网友的 源程序下载网
  • [nginx文档翻译系列] 控制nginx
  • “大数据应用场景”之隔壁老王(连载四)
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 2019年如何成为全栈工程师?
  • 3.7、@ResponseBody 和 @RestController
  • Android 控件背景颜色处理
  • axios 和 cookie 的那些事
  • CEF与代理
  • es6--symbol
  • HTTP那些事
  • mongodb--安装和初步使用教程
  • Redis的resp协议
  • 复习Javascript专题(四):js中的深浅拷贝
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 聊聊sentinel的DegradeSlot
  • 悄悄地说一个bug
  • 收藏好这篇,别再只说“数据劫持”了
  • 阿里云服务器购买完整流程
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • ## 基础知识
  • #传输# #传输数据判断#
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • $.ajax,axios,fetch三种ajax请求的区别
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (12)Hive调优——count distinct去重优化
  • (2024,Flag-DiT,文本引导的多模态生成,SR,统一的标记化,RoPE、RMSNorm 和流匹配)Lumina-T2X
  • (九十四)函数和二维数组
  • (转)Windows2003安全设置/维护
  • (转)平衡树
  • **CI中自动类加载的用法总结
  • .net CHARTING图表控件下载地址
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .net web项目 调用webService
  • .NET8 动态添加定时任务(CRON Expression, Whatever)
  • .net反混淆脱壳工具de4dot的使用
  • .net中的Queue和Stack
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [ 蓝桥杯Web真题 ]-布局切换