C++ 学习 多线程 2025年6月17日18:41:30

多线程(标准线程库 <thread>)

创建线程

#include <iostream>
#include <thread>void hello() {std::cout << "Hello from thread!\n";
}int main() {// 创建线程并执行 hello() std::thread t(hello); //线程对象,传入可调用对象(函数、Lambda、函数对象)t.join();              // 等待线程结束 阻塞主线程,直到子线程完成。
//t.detach():分离线程(线程独立运行,主线程不等待)。
// 仅在明确不需要管理线程生命周期时使用 detach(),并确保资源安全。return 0;
}
传递参数(和平常函数调用不同 注意看):
void print_sum(int a, int b) {std::cout << a + b << "\n";
}int main() {std::thread t(print_sum, 10, 20);  // 传递参数t.join();
}
Lambda 表达式 线程:
std::thread t([] {std::cout << "Lambda thread\n";
});
t.join();

线程同步:

互斥锁(Mutex):防止多个线程访问 共享数据:
#include <mutex>std::mutex mtx;
int shared_data = 0;//原始手动加锁 
void increment() {mtx.lock();          // 加锁:如果其他线程已锁,这里会阻塞等待shared_data++;       // 临界区:唯一线程能执行的代码mtx.unlock();        // 解锁:允许其他线程进入
}
//风险:如果 shared_data++ 抛出异常,unlock() 可能不被执行,导致死锁。void safe_increment() {std::lock_guard<std::mutex> lock(mtx);  // 构造时自动加锁shared_data++;                          // 临界区
} // 析构时自动解锁(即使发生异常)
//RAII(资源获取即初始化):利用对象生命周期自动管理锁,避免忘记解锁。加锁后的正确流程
线程A加锁 → shared_data++(变为1)→ 解锁线程B加锁 → shared_data++(变为2)→ 解锁
结果:shared_data = 2。
高阶用法:
void flexible_increment() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟加锁// ...其他非临界区代码...lock.lock();       // 手动加锁shared_data++;lock.unlock();     // 可手动提前解锁
}=================================================//多个锁时,按固定顺序获取:
std::mutex mtx1, mtx2;void safe_operation() {std::lock(mtx1, mtx2); // 同时加锁(避免死锁)std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);// 操作多个共享资源
}=============错误示范===============
int* get_data() {std::lock_guard<std::mutex> lock(mtx);return &shared_data; // ❌ 危险!锁失效后仍可访问
}注意事项
锁粒度:锁的范围应尽量小(减少阻塞时间)。避免嵌套锁:容易导致死锁。不要返回锁保护的指针/引用:会破坏封装性。
线程之间通知机制 (条件变量)
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;  // 条件变量依赖的共享状态// 消费者线程(等待数据)
void consumer() {std::unique_lock<std::mutex> lock(mtx);std::cout << "消费者: 等待数据...\n";cv.wait(lock, [] { return data_ready; });  // 等待条件成立std::cout << "消费者: 收到数据,开始处理!\n";
}// 生产者线程(准备数据)
void producer() {std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟耗时操作{std::lock_guard<std::mutex> lock(mtx);data_ready = true;  // 修改共享状态std::cout << "生产者: 数据已准备!\n";}cv.notify_one();  // 通知等待的消费者线程
}int main() {std::thread t1(consumer);  // 消费者线程(等待)std::thread t2(producer);  // 生产者线程(通知)t1.join();t2.join();return 0;
}//输出效果 
消费者: 等待数据...
生产者: 数据已准备!
消费者: 收到数据,开始处理!关键点解析
条件变量 (std::condition_variable)用于线程间的条件同步,允许线程阻塞直到某个条件成立。必须与 std::mutex 和 一个共享状态变量(如 bool data_ready)配合使用。
-------------------------------------------------------------------------
cv.wait(lock, predicate) 的工作原理步骤1:线程获取锁后检查条件(predicate)。步骤2:若条件为 false,线程释放锁并进入阻塞状态,等待通知。步骤3:当其他线程调用 notify_one() 时,线程被唤醒,重新获取锁并再次检查条件。步骤4:若条件为 true,线程继续执行;否则继续等待。为什么需要 data_ready 变量?避免虚假唤醒:操作系统可能意外唤醒线程,因此需要显式检查条件。状态同步:明确线程间的通信意图(如“数据已准备好”)。锁的作用域生产者:修改 data_ready 时必须加锁(lock_guard)。消费者:wait() 会自动释放锁,唤醒后重新获取锁。

异步任务(得重点掌握):

std::async

异步执行函数,返回 std::future

#include <future>int compute() { return 42; }int main() {std::future<int> result = std::async(compute);std::cout << "Result: " << result.get() << "\n";  // 阻塞获取结果
}std::launch::async:立即异步执行。std::launch::deferred:延迟到 get() 时执行。

=====================================================================

std::packaged_task

将函数包装为可异步调用的任务:

std::packaged_task<int()> task([] { return 7 * 6; }); //异步包装
std::future<int> result = task.get_future(); //future 用于稍后获取异步结果。
std::thread t(std::move(task));  // 在线程中执行
t.join();
std::cout << "Result: " << result.get() << "\n";==================================================
通过 std::move:将 task 的所有权转移给线程 t,避免拷贝。转移后,原 task 对象变为 空状态(不能再调用)

线程管理 

获取硬件线程数:

std::thread::hardware_concurrency()

 返回的是 当前计算机硬件支持的线程并发数(通常等于逻辑CPU核心数)

线程不超过线程数时 效果最佳

unsigned cores = std::thread::hardware_concurrency();
std::cout << "CPU cores: " << cores << "\n";
//std::thread::hardware_concurrency() 返回的是 
//当前计算机硬件支持的线程并发数(通常等于逻辑CPU核心数)4 核 4 线程 CPU → 输出 44 核 8 线程 CPU → 输出 8苹果 M1 Max (10 核) → 输出 10
线程局部储存(每个线程独享的变量TLS):
thread_local int counter = 0;  // 每个线程有独立副本

原子操作(无须锁的安全操作)

为什么不需要锁?

1.硬件支持

  • CPU 原子指令:现代 CPU 提供专门的指令(如 x86 的 LOCK XADD、ARM 的 LDREX/STREX)确保单条指令完成“读取-修改-写入”操作,不会被线程切换打断。

  • 缓存一致性协议:通过 MESI 等协议保证多核间对原子变量的可见性。


2. 编译器与语言标准保障

  • 编译器屏障std::atomic 操作会阻止编译器重排序相关指令。

  • 内存顺序控制:支持灵活的内存序(如 memory_order_relaxedmemory_order_seq_cst),平衡性能与一致性需求。

#include <iostream>
#include <atomic>
#include <thread>std::atomic<int> counter(0);void increment(int n) {for (int i = 0; i < n; ++i) {counter++;  // 原子自增}
}int main() {std::thread t1(increment, 100000);std::thread t2(increment, 100000);t1.join(); t2.join();std::cout << "Counter: " << counter << "\n";  // 保证输出 200000return 0;
}
特性std::atomicstd::mutex
实现层级硬件指令 + 编译器优化操作系统级锁(可能涉及系统调用)
粒度单个变量操作保护任意代码块
性能极高(无锁设计)较高(存在锁争用开销)
适用场景简单变量(int、bool、指针等)复杂逻辑或跨多个变量的操作

 

std::atomic 的局限性
  • 仅适用于标量类型:对结构体等复杂类型需自定义或使用锁。

  • 内存序选择:错误的内存序可能导致意外行为(如 memory_order_relaxed 不保证顺序)。

死锁预防:

避免嵌套锁:按固定顺序加锁。

使用 std::lock 同时锁多个互斥量(前面互斥锁有拓展)

std::mutex mtx1, mtx2;
std::lock(mtx1, mtx2);  // 同时加锁(避免死锁)
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

线程池(运用第三方库实现)广泛应用于需要高并发处理短任务的场景(如HTTP服务器、并行计算等)

第三方库(如 BS::thread_pool):
#include "thread_pool.hpp"          // 引入线程池库头文件
BS::thread_pool pool;              // 创建默认线程池(线程数=硬件并发数)
auto task = pool.submit([] { return 42; });  // 提交Lambda任务
std::cout << task.get() << "\n";   // 阻塞等待并获取结果
BS::thread_pool线程池类,管理一组工作线程(通常数量=CPU核心数)。
pool.submit()提交任务(函数/Lambda)到线程池,返回 std::future 对象。
task.get()阻塞调用线程,直到任务完成并返回结果(类似 std::future::get)。

 

工作流程
  1. 线程池初始化

    • 创建时默认启动 std::thread::hardware_concurrency() 个工作线程。

    • 线程空闲时会自动从任务队列中取任务执行。

  2. 任务提交

    • submit 将 Lambda [] { return 42; } 封装为任务,放入队列。

    • 返回的 task 是一个 std::future<int>,用于后续获取结果。

  3. 结果获取

    • task.get() 会阻塞主线程,直到某个工作线程完成该任务。

    • 最终输出 42

对比原生 std::thread
特性BS::thread_poolstd::thread
线程管理自动复用线程(避免频繁创建/销毁)需手动管理线程生命周期
任务队列支持批量提交任务需自行实现任务队列
开销低(线程复用)高(每次任务新建线程)
适用场景大量短任务少量长任务
 拓展用法示例:
//批量提交任务
std::vector<std::future<int>> results;
for (int i = 0; i < 10; ++i) {results.push_back(pool.submit([i] { return i * i; }));
}
for (auto& r : results) {std::cout << r.get() << " ";  // 输出 0 1 4 9 16 25 36 49 64 81
}//获取线程池信息
std::cout << "线程数: " << pool.get_thread_count() << "\n";//等待所有任务完成
pool.wait();  // 阻塞直到所有任务完成(不销毁线程)

总结

功能工具头文件
线程创建std::thread<thread>
互斥锁std::mutexstd::lock_guard<mutex>
条件变量std::condition_variable<condition_variable>
异步任务std::asyncstd::future<future>
原子操作std::atomic<atomic>
线程局部存储thread_local语言内置

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

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

相关文章

常见的测试工具及分类

Web测试工具是保障Web应用质量的核心支撑&#xff0c;根据测试类型&#xff08;功能、性能、安全、自动化等&#xff09;和场景需求&#xff0c;可分为多个类别。以下从​​八大核心测试类型​​出发&#xff0c;梳理常见工具及其特点、适用场景&#xff1a; ​​一、功能测试工…

七牛存储sdk在springboot完美集成和应用 七牛依赖 自动化配置

文章目录 概要依赖配置属性配置类配置文件业务层控制层运行结果亮点 概要 七牛存储很便宜的&#xff0c;在使用项目的用好官方封装好的sdk&#xff0c;结合springboot去使用很方便&#xff0c;我本地用的是springoot3spring-boot-autoconfigure 依赖 <dependency><…

Java相关-链表-设计链表-力扣707

你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则还需要属性 prev 以指示链表中的上一个节点…

C# 关于LINQ语法和类型的使用

常用语法&#xff0c;具体问题具体分析 1. Select2. SelectMany3. Where4. Take5. TakeWhile6. SkipWhile7. Join8. GroupJoin9. OrderBy10. OrderByDescending11. ThenBy12. Concat13. Zip14. Distinct15. Except16. Union17. Intersect18. Concat19. Reverse20. SequenceEqua…

华为OD-2024年E卷-小明周末爬山[200分] -- python

问题描述&#xff1a; 题目描述 周末小明准备去爬山锻炼&#xff0c;0代表平地&#xff0c;山的高度使用1到9来表示&#xff0c;小明每次爬山或下山高度只能相差k及k以内&#xff0c;每次只能上下左右一个方向上移动一格&#xff0c;小明从左上角(0,0)位置出发 输入描述 第一行…

Android:使用OkHttp

1、权限&#xff1a; <uses-permission android:name"android.permission.INTERNET" /> implementation com.squareup.okhttp3:okhttp:3.4.1 2、GET&#xff1a; new XXXTask ().execute("http://192.168.191.128:9000/xx");private class XXXTask…

Vue3+Element Plus动态表格列宽设置

在 Vue3 Element Plus 中实现动态设置表格列宽&#xff0c;可以通过以下几种方式实现&#xff1a; 方法 1&#xff1a;动态绑定 width 属性&#xff08;推荐&#xff09; vue 复制 下载 <template><el-table :data"tableData" style"width: 100%…

【JVM目前使用过的参数总结】

JVM参数总结 笔记记录 JVM-栈相关JVM-方法区(元空间)相关JVM-堆相关 JVM-栈相关 .-XX:ThreadStackSize1M -Xss1m 上面的简写形式【设置栈的大小】 JVM-方法区(元空间)相关 -XX:MaxMetaspaceSize10m 【设置最大元空间大小】 JVM-堆相关 -XX:MaxHeapSize10m -Xmx10m 上面的简写形…

AI辅助高考志愿填报-专业全景解析与报考指南

高考志愿填报&#xff0c;这可是关系到孩子未来的大事儿&#xff01;最近&#xff0c;我亲戚家的孩子也面临着这个难题&#xff0c;昨晚一个电话就跟我聊了好久&#xff0c;问我报啥专业好。说实话&#xff0c;这问题真不好回答&#xff0c;毕竟每个孩子情况不一样&#xff0c;…

Android Studio Windows安装与配置指南

Date: 2025-06-14 20:07:12 author: lijianzhan 内容简介 文章中&#xff0c;主要是为了初次接触 Android 开发的用户提供详细的关于 Android Studio 安装以及配置教程&#xff0c;涵盖环境准备、软件下载、安装配置全流程&#xff0c;重点解决路径命名、组件选择、工作空间设置…

SpringAI+DeepSeek-了解AI和大模型应用

一、认识AI 1.人工智能发展 AI&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;使机器能够像人类一样思考、学习和解决问题的技术。 AI发展至今大概可以分为三个阶段&#xff1a; 其中&#xff0c;深度学习领域的自然语言处理(Natural Lan…

IP5362至为芯支持无线充的22.5W双C口双向快充移动电源方案芯片

英集芯IP5362是一款应用于移动电源&#xff0c;充电宝&#xff0c;手机&#xff0c;平板电脑等支持无线充模式的22.5W双向快充移动电源方案SOC芯片,集成同步升降压转换器、锂电池充电管理、电池电量指示等功能。兼容全部快充协议&#xff0c;同步开关放电支持最大22.5W输出功率…

手游刚开服就被攻击怎么办?如何防御DDoS?

手游新上线时遭遇DDoS攻击是常见现象&#xff0c;可能导致服务器瘫痪、玩家流失甚至项目失败。面对突如其来的攻击&#xff0c;开发者与运营商需要迅速响应并建立长效防御机制。本文提供应急处理步骤与防御策略&#xff0c;助力游戏稳定运营。 一、手游开服遭攻击的应急响应 快…

秋招是开发算法一起准备,还是只准备一个

THE LAST TIME 昨天晚上半夜有个星球的26届的同学&#xff0c;私信问我。说目前是只准备开发还是开发算法一起准备&#xff08;两者技术知识都挺欠缺的&#xff09; 看到这里&#xff0c;肯定有很多同学会说。马上都该秋招了&#xff0c;还什么多线程开工&#xff0c;赶紧能住编…

web项目部署配置HTTPS遇到的问题解决方法

今天使用nginxtomcatssl完成了web项目的部署&#xff0c;本以为没有什么问题&#xff0c;但是在页面测试的时候又蹦出了这么一个问题&#xff0c;大致是说由于配置了HTTPS&#xff0c;但是之前的请求是通过HTTP请求的&#xff0c;所以现在被拦截&#xff0c;由于缺少某些权限信…

理解与建模弹性膜-AI云计算数值分析和代码验证

弹性膜在连接生物学理解和工程创新方面至关重要&#xff0c;因为它们能够模拟软组织力学、实现先进的细胞培养系统和促进柔性设备&#xff0c;广泛应用于软组织生物力学、细胞培养、生物膜建模和生物医学工程等领域。 ☁️AI云计算数值分析和代码验证 弹性膜在连接生物学理解和…

AI大模型竞赛升温:百度发布文心大模型4.5和X1

AI大模型&#xff0c;作为智能技术的巅峰之作&#xff0c;正逐步改变着我们的生活与工作方式。近期&#xff0c;百度在AI大模型领域的最新动向&#xff0c;无疑为这场科技竞赛再添一把火。3月16日&#xff0c;百度正式宣布发布文心大模型4.5及文心大模型X1&#xff0c;这两款大…

升级OpenSSL和OpenSSH 修复漏洞

升级OpenSSL和OpenSSH 目前版本OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017 升级到OpenSSH_9.8p1, OpenSSL 1.1.1u 30 May 2023 服务器CentOS Linux release 7.6.1810 (Core) 一、升级OpenSSL到1.1.1u 下载并编译 OpenSSL&#xff08;推荐目录 /usr/local/openssl&…

JavaSE - Object 类详细讲解

定义 是所有类的直接或者间接父类&#xff0c;是 Java 中唯一一个没有父类的类。其中所有的方法都是可以被子类继承的。 常用方法 equals方法&#xff1a; 比较两个对象引用的地址值是否相同&#xff0c;默认情况下是使用 “” 进行比较&#xff0c;但是这个方法一般会被之类…

观远ChatBI|让数据分析像聊天一样简单

BI通过收集、整合和分析企业内部的各种数据&#xff0c;帮助企业发现数据中的模式和趋势&#xff0c;从而做出更明智的商业决策&#xff0c;以此来提升企业的经营能力和竞争力。无论是传统BI还是自助BI&#xff0c;都是为了在数据和人之间建立一座桥梁&#xff0c;使数据能够被…