C++多线程编程:跨线程操作全解析

C++中的"线程"通常指单个执行流(如std::thread对象),而"多线程"指程序中同时存在多个这样的执行流,并涉及它们的创建、管理和同步。实现跨线程操作的核心在于安全地处理共享数据线程间通信

以下是实现跨线程操作的主要方法:

1. 🛡️ 使用互斥锁 (Mutex) 保护共享数据

互斥锁用于确保同一时间只有一个线程能访问共享资源,防止数据竞争。

  • std::mutex: 基本互斥锁,需手动管理锁的获取和释放。

    #include <iostream>#include <thread>#include <mutex>std::mutex mtx;// 全局互斥量int shared_data = 0;void increment() {mtx.lock();// 请求锁定互斥量++shared_data;// 修改共享资源mtx.unlock();// 释放互斥量
    }

    更推荐使用RAII机制自动管理锁:

  • std::lock_guard: 在构造时加锁,析构时自动解锁,适用于简单作用域。

    void safe_increment() {std::lock_guard<std::mutex> lock(mtx);// 自动锁定和解锁++shared_data;
    }// lock_guard析构,mtx自动解锁
  • std::unique_lock: 比lock_guard更灵活,支持延迟加锁、条件变量等。

    void safe_increment_flex() {std::unique_lock<std::mutex> ul(mtx);++shared_data;ul.unlock();// 可手动提前解锁// ... 执行其他不操作共享数据的任务
    }

2. 🔄 使用条件变量 (Condition Variable) 进行线程间协调

条件变量允许一个或多个线程等待某个条件成立后再继续执行,常用于生产者-消费者模型

  • std::condition_variable: 线程等待特定条件被满足。

    #include <iostream>#include <thread>#include <mutex>#include <condition_variable>#include <queue>std::mutex mtx;
    std::condition_variable cv;
    std::queue<int> data_queue;
    bool ready = false;// 生产者线程void producer() {std::this_thread::sleep_for(std::chrono::seconds(1));{std::lock_guard<std::mutex> lk(mtx);data_queue.push(42);ready = true;}cv.notify_one();// 通知一个等待的消费者线程
    }// 消费者线程void consumer() {std::unique_lock<std::mutex> lk(mtx);
    // 等待条件满足:lambda函数返回false时,线程会阻塞并释放锁cv.wait(lk, []{ return ready; });
    // 条件满足后,重新获得锁,继续执行int data = data_queue.front();data_queue.pop();std::cout << "Consumed: " << data << std::endl;
    }int main() {std::thread prod(producer);std::thread cons(consumer);prod.join();cons.join();return 0;
    }

3. ⚡ 使用原子操作 (Atomic Operations)

原子操作确保对基本数据类型(如intbool)的读写操作是不可中断的,无需加锁,性能更高。

  • std::atomic: 模板类,提供原子操作。

    #include <atomic>#include <thread>std::atomic<int> atomic_counter(0);void safe_increment_atomic() {for (int i = 0; i < 1000; ++i) {++atomic_counter;// 原子操作,线程安全}
    }int main() {std::thread t1(safe_increment_atomic);std::thread t2(safe_increment_atomic);t1.join();t2.join();std::cout << atomic_counter << std::endl;// 输出2000return 0;
    }

4. 🚀 使用异步操作 (Async Operations)

std::asyncstd::future/std::promise可以更方便地获取另一个线程的执行结果,实现线程间数据传递。

  • std::asyncstd::future: 异步执行任务并获取返回值。

    #include <iostream>#include <future>int heavy_calculation() {
    // 模拟耗时计算return 42;
    }int main() {
    // 异步启动任务,返回一个future对象std::future<int> fut = std::async(std::launch::async, heavy_calculation);
    // ... 主线程可以同时做其他工作int result = fut.get();// 获取异步任务的结果(如果未完成会等待)std::cout << "Result: " << result << std::endl;return 0;
    }
  • std::promisestd::future: 在线程间传递数据。

    #include <iostream>#include <thread>#include <future>void worker(std::promise<int> prom) {
    // 做一些工作...prom.set_value(123);// 设置值,通知future
    }int main() {std::promise<int> prom;std::future<int> fut = prom.get_future();std::thread t(worker, std::move(prom));int value = fut.get();// 等待并获取worker线程设置的值std::cout << "Received: " << value << std::endl;t.join();return 0;
    }

📊 方法对比与选择建议

方法适用场景优点缺点
互斥锁 (Mutex)保护复杂的共享数据结构或代码段通用性强,可保护任意临界区锁竞争可能导致性能开销和死锁风险
条件变量 (Condition Variable)线程间等待特定条件发生(生产者-消费者)高效协调线程执行顺序需与互斥锁配合使用,逻辑稍复杂
原子操作 (Atomic)简单的计数器、状态标志更新高性能,无锁编程仅适用于基本数据类型,逻辑复杂时易出错
异步操作 (Async/Future)需要获取另一个线程的结果简化线程间值传递和同步不适合需要持续通信或复杂同步的场景

选择哪种方法取决于你的具体需求:

  • 需要对复杂数据结构或代码块进行排他性访问时,使用互斥锁(优先搭配lock_guardunique_lock)。
  • 需要让线程等待某个条件成立(如任务队列非空)时,使用条件变量
  • 只需安全地修改一个整数、布尔值等简单变量时,使用原子操作
  • 需要获取另一个线程的计算结果时,使用**std::asyncstd::future**。

💡 重要注意事项

  1. 避免死锁:确保锁的获取和释放顺序一致,或使用std::lock一次性锁定多个互斥量。
  2. 线程管理:使用join()等待线程完成,或使用detach()分离线程(需谨慎,分离后无法再控制线程)。
  3. 数据传递:向线程传递引用参数时,需使用std::ref(确保对象生命周期长于线程),或直接按值传递。

实现安全高效的跨线程操作,关键在于根据场景选择合适的同步机制,并谨慎管理共享数据的访问。

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

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

相关文章

【脑电分析系列】第13篇:脑电源定位:从头皮到大脑深处,EEG源定位的原理、算法与可视化

前言脑电信号&#xff08;Electroencephalography, EEG&#xff09;是一种非侵入性的神经成像技术&#xff0c;能够实时捕捉大脑的电活动。然而&#xff0c;头皮上记录到的信号是脑源活动经过头皮、颅骨等介质“模糊”后的投影。想要从这些头皮EEG信号追溯到大脑深处的电活动&a…

MySQL知识笔记

DATE_ADD(date,INTERVAL expr type) date 参数是合法的日期表达式。expr 参数是您希望添加的时间间隔。多查官方手册&#xff01;&#xff01;命令行启动和停止sql服务net start 数据库名&#xff1b; 这是启动服务命令&#xff1b; 例如&#xff1a;net start Mysql56…

2025算法八股——深度学习——MHA MQA GQA

MHA、MQA、GQA 都是深度学习中注意力机制的相关概念&#xff0c;其中 MHA 是标准的多头注意力机制&#xff0c;MQA 和 GQA 则是其优化变体&#xff0c;以下是它们的区别、优缺点介绍&#xff1a;区别MHA&#xff08;多头注意力&#xff09;&#xff1a;是 Transformer 架构的核…

Vue3》》eslint Prettier husky

安装必要的依赖 npm install -D eslint eslint/js vue/eslint-config-prettier prettier eslint-plugin-vue 初始化 ESLint 配置 npm init eslint/config// eslint.config.js // 针对 JavaScript 的 ESLint 配置和规则。保持 JavaScript 代码的一致性和质量 import js from &qu…

Custom SRP - Point and Spot Lights

https://catlikecoding.com/unity/tutorials/custom-srp/point-and-spot-lights/Lights with Limited Influence1 Point Lights1.1 Other Light Data (Point )同方向光一样,我们支持有限数量的 Other Light.尽管场景中可能有很多 Other Lights,可能有超过光源上限的光源时可见的…

hive数据仓库的搭建

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、内嵌模式二、本地模式三、远程模式前言 HIVE是基于HDFS的数据仓库&#xff0c;要首先搭建好HADOOP的集群才可以正常使用HIVE&#xff0c;HADOOP集运搭建详见…

域名SSL证书免费申请lcjmSSL

.-.lcjmSSL&#xff08;又名“来此加密”&#xff09;是一个提供免费SSL证书申请的一站式平台。它支持单域名、多域名以及泛域名证书申请&#xff0c;且单张证书最高可覆盖100个域名&#xff0c;让您轻松实现全站HTTPS加密。为什么您的网站必须安装SSL证书&#xff1f;数据加密…

“能量逆流泵”:一种基于电容阵与开关矩阵的超高效大功率降压架构

摘要本文提出并验证了一种面向大功率降压应用的革命性电源架构——"能量逆流泵"&#xff08;Energy Inversion Pump, EIP&#xff09;。该架构摒弃了传统Buck转换器中的电感元件&#xff0c;通过高速开关矩阵控制的电容阵列&#xff0c;将高压侧能量以"分时、分…

打造精简高效的 uni-app 网络请求工具

在 uni-app 开发中&#xff0c;网络请求是连接前端与后端的核心桥梁。一个设计良好的请求工具能够显著提升开发效率&#xff0c;减少重复代码。本文将分享一个精简版的 uni-app 网络请求工具实现&#xff0c;它保留了核心功能同时保持了足够的灵活性。设计思路一个优秀的网络请…

【面试场景题】交易流水表高qps写入会有锁等待或死锁问题吗

文章目录一、先明确交易流水表的核心特性二、InnoDB的锁机制在流水表写入场景的表现1. 行锁&#xff08;Record Lock&#xff09;&#xff1a;基本不涉及2. 间隙锁&#xff08;Gap Lock&#xff09;与Next-Key Lock&#xff1a;几乎不触发3. 表锁&#xff1a;仅在极端场景出现三…

项目部署——LAMP、LNMP和LTMJ

前情提要问&#xff1a;如何通过nginx的反向代理&#xff0c;代理多台虚拟主机&#xff08;一台apache服务器上的虚拟主机&#xff09;&#xff1f;1.在nginx的配置文件中&#xff0c;将基于域名的访问改为基于端口的访问&#xff08;nginx.conf&#xff09;upstream daili{ser…

晨曦中,它已劳作:一台有温度的机器人如何重塑我们的洁净日常

清晨六点&#xff0c;城市的轮廓在微光中逐渐清晰。某高端小区的路面上&#xff0c;一台灰色机身、线条流畅的机器正在安静地工作。它绕过停靠的车辆&#xff0c;精准地沿着路缘石前进&#xff0c;吸走落叶与尘土&#xff0c;遇到突然窜出的流浪猫时轻巧避让&#xff0c;仿佛有…

【最新高级版】酷柚易汛生产管理系统v1.2.8 +uniapp全开源+文档教程

酷柚易汛生产管理系统是基于FastAdminThinkPHPLayuiuniapp开发的生产管理系统&#xff0c;帮助企业数字化转型&#xff0c;打造智能工厂&#xff0c;专业为生产企业量身开发的一套完整的生产管理系统。主要包含以下模块&#xff1a;购货模块、生产模块、仓库模块、资料模块&…

40分钟的Docker实战攻略

一&#xff1a;什么是Docker &#xff08;1&#xff09;基本概念 Docker 是一种开源的 容器化平台&#xff0c;用于快速构建、部署和运行应用程序。它通过将应用程序及其依赖项打包到轻量级的、可移植的容器中&#xff0c;实现了环境一致性&#xff0c;解决了“在我机器上能运…

qt使用camke时,采用vcpkg工具链设置OSG的qt模块osgQOpenGLWidget

【免费】osgQOpenGLWidget嵌入qt模块,VS2022使用cmake的方式,工具链vcpkg资源-CSDN下载 CMake中设置 1.查找osg相关的库,同时也会设置对应include的路径 # 检查是否找到 osg find_package(OpenSceneGraph 3.6.5REQUIRED COMPONENTS osgosgUtilosgGAosgViewerosgDBosgAnimatio…

洛谷 P2245 星际导航(kruskal 重构树 + 倍增优化求路径最大边权)

题目链接 题目难度 洛谷上是蓝题&#xff0c;我觉得这道题挺简单的&#xff0c;一眼就看穿了&#xff0c;应该是绿题。 题目解法概括 kruskal 重构树 倍增优化求路径最大边权。 代码 #include <iostream> #include <vector> #include <algorithm> #in…

STM32H743-ARM例程1-IDE环境搭建与调试下载

目录实验平台环境搭建一、Keil MDK集成开发环境1.MDK简介2.MDK5安装3.程序下载与调试二、STM32CubeMX1.STM32CubeMX简介2.JAVA JRE安装3.STM32CubeMX安装4.STM32CubeH7库安装实验平台 硬件&#xff1a;银杏科技GT7000双核心开发板-ARM-STM32H743XIH6&#xff0c;银杏科技iTool…

FPGA学习篇——Verilog学习MUX的实现

PS&#xff1a;目前手上仍然没有板子&#xff0c;按照野火视频的讲解&#xff0c;目前我们只能做到前面六步&#xff08;其实第一步设计规划也是需要看板子的硬件的&#xff0c;但是现在没有板子就完全与野火传授的板子一致来看&#xff09; 首先我们以最简单的2路选择器MUX2_1…

OpenStack 学习笔记

OpenStack 1. 什么是 OpenStack 1.1 OpenStack 发展史 2006 年亚马逊推出 AWS&#xff0c;正式开启云计算的新纪元 2010 年 7 月美国国家航空航天局&#xff08;NASA&#xff09;与 Rackspace 合作&#xff0c;共同宣布 OpenStack 开放源码计划&#xff0c;由此开启了属于 Open…

mysql小数取整

1 向下取整 SELECT FLOOR(123.456); -- 结果: 1232 向上取整 SELECT CEIL(123.001); -- 结果: 1243 四舍五入 SELECT ROUND(123.456); -- 结果: 123 SELECT ROUND(123.556); -- 结果: 1244 截断&#xff08;不四舍五入&#xff0c;直接截断小数位&#xff09; SELECT …