第12章:优化并发_《C++性能优化指南》notes
优化并发
- 一、并发基础与优化核心知识点
- 二、关键代码示例与测试
- 三、关键优化策略总结
- 四、性能测试方法论
- 多选题
- 设计题
- 答案与详解
- 多选题答案:
- 设计题答案示例
一、并发基础与优化核心知识点
- 线程 vs 异步任务
- 核心区别:
std::thread直接管理线程,std::async由运行时决定异步策略(可能用线程池)。 - 优化点:频繁创建线程开销大,优先用
std::async。
- 原子操作与内存序
- 原子类型:
std::atomic<T>确保操作不可分割。 - 内存序:
memory_order_relaxed(无同步)到memory_order_seq_cst(全序同步)。
- 锁的精细控制
- 锁类型:
std::mutex、std::recursive_mutex、std::shared_mutex。 - 优化技巧:缩短临界区、避免嵌套锁、用
std::lock_guard/std::unique_lock自动管理。
- 条件变量与生产者-消费者
- 使用模式:
wait()搭配谓词防止虚假唤醒,notify_one()/notify_all()通知。
- 无锁数据结构
- 适用场景:高并发计数器、队列等,减少锁竞争。
二、关键代码示例与测试
示例1:原子操作 vs 互斥锁的性能对比
#include <iostream>
#include <atomic>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>constexpr int kIncrements = 1'000'000;// 使用互斥锁的计数器
struct MutexCounter {int value = 0;std::mutex mtx;void increment() {std::lock_guard<std::mutex> lock(mtx);++value;}
};// 使用原子操作的计数器
struct AtomicCounter {std::atomic<int> value{0};void increment() {value.fetch_add(1, std::memory_order_relaxed);}
};template<typename Counter>
void test_performance(const std::string& name) {Counter counter;auto start = std::chrono::high_resolution_clock::now();std::vector<std::thread> threads;for (int i = 0; i < 4; ++i) {threads.emplace_back([&counter] {for (int j = 0; j < kIncrements; ++j) {counter.increment();}});}for (auto& t : threads) t.join();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();std::cout << name << " Result: " << counter.value << ", Time: " << duration << "ms\n";
}int main() {test_performance<MutexCounter>("Mutex Counter");test_performance<AtomicCounter>("Atomic Counter");return 0;
}
编译命令:
g++ -std=c++11 -O2 -pthread atomic_vs_mutex.cpp -o atomic_vs_mutex
输出示例:
Mutex Counter Result: 4000000, Time: 182ms
Atomic Counter Result: 4000000, Time: 32ms
结论:原子操作在高并发下性能显著优于互斥锁。
示例2:线程池实现(减少线程创建开销)
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>class ThreadPool {
public:ThreadPool(size_t num_threads) : stop(false) {for (size_t i = 0; i < num_threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task();}});}}template<class F, class... Args>auto enqueue(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {using return_type = decltype(f(args...));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);tasks.emplace([task] { (*task)(); });}condition.notify_one();return res;}~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread& worker : workers)worker.join();}private:std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop;
};int main() {ThreadPool pool(4);std::vector<std::future<int>> results;for (int i = 0; i < 8; ++i) {results.emplace_back(pool.enqueue([i] {std::cout << "Task " << i << " executed\n";return i * i;}));}for (auto& result : results)std::cout << "Result: " << result.get() << std::endl;return 0;
}
编译命令:
g++ -std=c++11 -O2 -pthread thread_pool.cpp -o thread_pool
输出示例:
Task Result: Task 3Task 1Task 20 executedexecuted
Task 5 executed
Task 40 executed
Task 7Task 6 executedexecutedexecutedexecutedResult: 1
Result: 4
Result: 9
Result: 16
Result: 25
Result: 36
Result: 49
结论:线程池复用线程,减少频繁创建销毁的开销。
示例3:使用无锁队列提升并发
#include <iostream>
#include <atomic>
#include <thread>template<typename T>
class LockFreeQueue {
public:struct Node {T data;Node* next;Node(const T& data) : data(data), next(nullptr) {}};LockFreeQueue() : head(new Node(T())), tail(head.load()) {}void push(const T& data) {Node* newNode = new Node(data);Node* prevTail = tail.exchange(newNode);prevTail->next = newNode;}bool pop(T& result) {Node* oldHead = head.load();if (oldHead == tail.load()) return false;head.store(oldHead->next);result = oldHead->next->data;delete oldHead;return true;}private:std::atomic<Node*> head;std::atomic<Node*> tail;
};int main() {LockFreeQueue<int> queue;// 生产者线程std::thread producer([&] {for (int i = 0; i < 10; ++i) {queue.push(i);std::this_thread::sleep_for(std::chrono::milliseconds(10));}});// 消费者线程std::thread consumer([&] {int value;while (true) {if (queue.pop(value)) {std::cout << "Consumed: " << value << std::endl;if (value == 9) break;}}});producer.join();consumer.join();return 0;
}
编译命令:
g++ -std=c++11 -O2 -pthread lockfree_queue.cpp -o lockfree_queue
输出示例:
Consumed: 0
Consumed: 1
Consumed: 2
Consumed: 3
Consumed: 4
Consumed: 5
Consumed: 6
Consumed: 7
Consumed: 8
Consumed: 9
结论:无锁队列减少锁争用,适合高并发场景。
三、关键优化策略总结
-
减少锁竞争:
- 缩小临界区范围。
- 使用读写锁(
std::shared_mutex)区分读写操作。
-
利用原子操作:
- 简单计数器优先用
std::atomic。 - 适当选择内存序(如
memory_order_relaxed)。
- 简单计数器优先用
-
异步与线程池:
- 避免频繁创建线程,使用
std::async或自定义线程池。
- 避免频繁创建线程,使用
-
无锁数据结构:
- 在CAS(Compare-And-Swap)安全时使用,但需注意ABA问题。
-
缓存友好设计:
- 避免false sharing(用
alignas对齐或填充字节)。
- 避免false sharing(用
四、性能测试方法论
-
基准测试:
- 使用
std::chrono精确测量代码段耗时。 - 对比不同实现的吞吐量(如ops/sec)。
- 使用
-
压力测试:
- 模拟高并发场景,观察资源竞争情况。
-
工具辅助:
- Valgrind检测内存问题。
- perf分析CPU缓存命中率。
多选题
-
下列哪些情况可能导致数据竞争?
A. 多个线程同时读取同一变量
B. 一个线程写,另一个线程读同一变量
C. 使用互斥量保护共享变量
D. 使用原子变量操作 -
关于
std::async和std::thread的选择,正确的说法是?
A.std::async默认启动策略是延迟执行
B.std::thread更适合需要直接控制线程生命周期的场景
C.std::async会自动管理线程池
D.std::thread无法返回计算结果 -
以下哪些操作可能引发死锁?
A. 在持有锁时调用外部未知代码
B. 对多个互斥量使用固定顺序加锁
C. 递归互斥量(std::recursive_mutex)的嵌套加锁
D. 未及时释放条件变量关联的锁 -
关于原子操作的内存顺序,正确的说法是?
A.memory_order_relaxed不保证操作顺序
B.memory_order_seq_cst会降低性能但保证全局顺序
C.memory_order_acquire仅保证读操作的可见性
D. 原子操作必须与volatile关键字结合使用 -
优化同步的合理手段包括:
A. 将大临界区拆分为多个小临界区
B. 使用无锁数据结构替代互斥量
C. 通过std::future传递计算结果
D. 在单核系统上使用忙等待(busy-wait) -
关于条件变量的正确使用方式:
A. 必须在循环中检查谓词
B.notify_one()比notify_all()更高效
C. 可以脱离互斥量单独使用
D. 虚假唤醒(spurious wakeup)是必须处理的 -
以下哪些是"锁护送"(Lock Convoy)的表现?
A. 多个线程频繁争抢同一互斥量
B. 线程因锁竞争频繁切换上下文
C. 互斥量的持有时间极短
D. 使用读写锁(std::shared_mutex) -
减少共享数据竞争的方法包括:
A. 使用线程局部存储(TLS)
B. 复制数据到各线程独立处理
C. 通过消息队列传递数据
D. 增加互斥量的数量 -
关于
std::promise和std::future的正确说法是:
A.std::promise只能设置一次值
B.std::future的get()会阻塞直到结果就绪
C. 多个线程可以共享同一个std::future对象
D.std::async返回的std::future可能延迟执行 -
关于原子变量与互斥量的对比,正确的说法是:
A. 原子变量适用于简单数据类型
B. 互斥量能保护复杂操作序列
C. 原子变量的fetch_add是原子的
D. 互斥量比原子变量性能更好
设计题
- 实现一个线程安全的无锁(lock-free)队列
要求:
- 使用原子操作实现
push和pop - 处理ABA问题
- 提供测试代码验证并发操作正确性
- 设计一个生产者-消费者模型
要求:
- 使用
std::condition_variable和std::mutex - 队列长度限制为固定大小
- 支持多生产者和多消费者
- 提供测试代码模拟并发场景
- 实现基于
std::async的并行任务执行器
要求:
- 支持提交任意可调用对象
- 自动回收已完成的任务资源
- 限制最大并发线程数为CPU核心数
- 测试代码展示并行加速效果
- 优化高竞争场景下的计数器
要求:
- 使用线程局部存储(TLS)分散写操作
- 定期合并局部计数到全局变量
- 对比普通原子计数器与优化版本的性能差异
- 提供测试代码和性能统计输出
5 实现一个读写锁(Read-Write Lock)
要求:
- 支持多个读者或单个写者
- 避免写者饥饿(writer starvation)
- 基于
std::mutex和std::condition_variable实现 - 测试代码验证读写操作的互斥性
答案与详解
多选题答案:
-
B
解析:数据竞争的条件是至少一个线程写且无同步措施。A只有读不冲突,C/D有同步机制。 -
B, D
解析:std::async默认策略非延迟(C++11起为std::launch::async|deferred),B正确,D因std::thread无直接返回值机制正确。 -
A, C
解析:A可能导致回调中再次加锁;C递归锁允许同一线程重复加锁但需对应解锁次数,误用仍可能死锁。 -
A, B
解析:memory_order_relaxed无顺序保证,B正确,C中acquire保证后续读的可见性,D错误(原子操作无需volatile)。 -
A, B, C
解析:D在单核忙等待浪费CPU,其余均为有效优化手段。 -
A, B, D
解析:C错误,条件变量必须与互斥量配合使用。 -
A, B
解析:锁护送表现为频繁争抢导致线程切换,C短持有时间反而减少竞争,D无关。 -
A, B, C
解析:D增加锁数量可能加剧竞争,其余均为减少竞争的有效方法。 -
A, B, D
解析:C错误,std::future不可多线程同时调用get()。 -
A, B, C
解析:D错误,互斥量在低竞争时性能可能更差。
设计题答案示例
- 无锁队列实现(部分代码)
#include <atomic>
#include <memory>template<typename T>
class LockFreeQueue {
private:struct Node {std::shared_ptr<T> data;std::atomic<Node*> next;Node() : next(nullptr) {}};std::atomic<Node*> head;std::atomic<Node*> tail;public:LockFreeQueue() : head(new Node), tail(head.load()) {}void push(T value) {Node* new_node = new Node;new_node->data = std::make_shared<T>(std::move(value));Node* old_tail = tail.load();while (!old_tail->next.compare_exchange_weak(nullptr, new_node)) {old_tail = tail.load();}tail.compare_exchange_weak(old_tail, new_node);}std::shared_ptr<T> pop() {Node* old_head = head.load();while (old_head != tail.load()) {if (head.compare_exchange_weak(old_head, old_head->next)) {std::shared_ptr<T> res = old_head->next->data;delete old_head;return res;}}return nullptr;}
};// 测试代码
int main() {LockFreeQueue<int> queue;queue.push(42);auto val = queue.pop();if (val && *val == 42) {std::cout << "Test passed!\n";}return 0;
}
- 生产者-消费者模型
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>template<typename T>
class SafeQueue {
private:std::queue<T> queue;std::mutex mtx;std::condition_variable cv;size_t max_size;public:SafeQueue(size_t size) : max_size(size) {}void push(T item) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this] { return queue.size() < max_size; });queue.push(std::move(item));cv.notify_all();}T pop() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this] { return !queue.empty(); });T val = std::move(queue.front());queue.pop();cv.notify_all();return val;}
};// 测试代码
int main() {SafeQueue<int> q(10);std::thread producer([&q] {for (int i = 0; i < 10; ++i) {q.push(i);}});std::thread consumer([&q] {for (int i = 0; i < 10; ++i) {int val = q.pop();std::cout << "Got: " << val << '\n';}});producer.join();consumer.join();return 0;
}
其他设计题目的答案, 稍后补充
相关文章:
第12章:优化并发_《C++性能优化指南》notes
优化并发 一、并发基础与优化核心知识点二、关键代码示例与测试三、关键优化策略总结四、性能测试方法论多选题设计题答案与详解多选题答案: 设计题答案示例 一、并发基础与优化核心知识点 线程 vs 异步任务 核心区别:std::thread直接管理线程…...
逼用户升级Win11,微软开始给Win10限速
随着Windows10的支持时间越来越短,微软也加大了对Win10用户的驱赶力度。 最近,微软官宣了将要在今年6月份降低OneNote for Windows 10的同步速度。软件也将和Windows10在今年的10月14日一同停止支持和维护。 这将影响实时协作和多设备访问。 对OneNote…...
HarmonyOs-ArkUI List组件
列表是一个复杂的容器,当列表项达到一定数量,使得列表内容超出其范围的时候,就会自动变为可以滚动。列表适合用来展现同类数据类型。 List组件支持使用,条件渲染,循环渲染,懒加载等渲染控制方式生成子组件…...
基于YOLOv8深度学习的PCB缺陷检测识别系统【python源码+GUI界面+数据集+训练代码+登录界面】
目录 一、界面全貌展示 二、前言摘要 三、GUI界面演示 (一)用户加载自定义模型 (二)单张图像检测 (三)检测图像文件夹 (四)检测视频 (五)摄像头检测 …...
鸿蒙生态圈暗战:数字孪生三强争霸谁将主宰消费电子未来?
IDC数据显示,2025年Q1华为以38.7%份额领跑中国折叠屏市场,Pura X首月销量突破120万台。这款搭载HarmonyOS 5的旗舰,通过灵犀通信技术实现5G A网络下载速率提升30%,并在离线环境下完成厘米级导航。其爆款逻辑背后,是鸿蒙…...
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析
一、React 15(2016) 核心架构:Stack Reconciler(栈协调器) 工作原理: 同步递归渲染:采用深度优先遍历方式递归处理 Virtual DOM,形成不可中断的调用栈渲染流程:1. 触发 …...
计算机网络 --应用层
计算机网络 --应用层 一、应用层概述 1. 功能 应用层为应用程序通信提供直接服务,这种服务是用户能够直接感知到的数据通信服务。核心功能包括: 文件传输:实现不同设备间文件的传输操作。访问管理:对用户访问资源等进行管理。电…...
CMS迁移中SEO优化整合步骤详解
内容概要 在CMS迁移过程中,系统化的规划与执行是保障SEO排名稳定性的核心。首先需明确迁移流程的关键阶段,包括数据备份、URL适配、元数据同步及安全配置等环节。其中,数据备份不仅需覆盖原始数据库与静态资源,还需验证备份文件的…...
数据结构初阶-二叉树链式
目录 1.概念与结构 2.二叉数链式的实现 2.1遍历规则 2.2申请内存空间 2.3手动构建一棵二叉树 2.4二叉树结点的个数 2.5二叉树叶子结点的个数 2.6二叉树第K层结点个数 2.7二叉树的高度 2.8二叉树中查找值为x的结点 2.9二叉树的销毁 3.层序遍历 3.1概念 3.2层序遍历…...
Springboot 集成 Flowable 6.8.0
1. 创建 Spring Boot 项目 通过 Spring Initializr(https://start.spring.io/ )创建一个基础的 Spring Boot 项目,添加以下依赖: Spring WebSpring Data JPAMySQL DriverLombok(可选,用于简化代码&#x…...
协作机械臂需要加安全墙吗? 安全墙 光栅 干涉区
安全墙是什么 文章目录 安全墙是什么简介1. 物理安全墙1.1 定义:1.2 作用机制:1.3 应用场景: 2. 虚拟安全墙2.2 定义:2.3 作用机制:2.3 应用场景: 3. 安全毛毯3.1 工作原理:3.2 特点3.3 应用场景…...
HTML5 SVG:图形绘制的现代标准
HTML5 SVG:图形绘制的现代标准 引言 随着互联网技术的发展,网页的交互性和美观性日益受到重视。HTML5 SVG作为一种强大的图形绘制技术,在网页设计中发挥着重要作用。本文将深入探讨HTML5 SVG的原理、应用场景以及如何在实际项目中运用。 一、HTML5 SVG简介 1.1 什么是SV…...
洛谷题单1-B2025 输出字符菱形-python-流程图重构
题目描述 用 * 构造一个对角线长 5 5 5 个字符,倾斜放置的菱形。 输入格式 没有输入要求。 输出格式 如样例所示。用 * 构成的菱形。 输入输出样例 #1 输入 #1 输出 #1 **** *********方式-前半区推导,后半区逆序 代码 class Solution:static…...
springboot+mybatisplus
1.什么是springboot? Spring Boot是一个用于快速构建Spring应用程序的框架。它旨在帮助开发人员快速搭建Spring框架,减少配置和繁琐的工作。Spring Boot继承了原有Spring框架的优秀基因,使Spring在使用中更加方便快捷。 在Spring Boot中集成ActiveMQ,需要导入相应的starter…...
《TypeScript 面试八股:高频考点与核心知识点详解》
“你好啊!能把那天没唱的歌再唱给我听吗? ” 前言 因为主包还是主要学习js,ts浅浅的学习了一下,在简历中我也只会写了解,所以我写一些比较基础的八股,如果是想要更深入的八股的话还是建议找别人的。 Ts基…...
Golang os模块功能详解与示例
os 是 Go 语言标准库中与操作系统交互的核心模块,提供了丰富的功能来操作文件系统、进程、环境变量等。下面我将详细介绍 os 模块的主要功能,并提供相应的代码示例。 1. 文件与目录操作 1.1 文件操作 创建文件 package mainimport ("fmt"&…...
SICAR 标准 KUKA 机器人标准功能块说明手册
功能块名称:LSicar_Robot_KUKA_PrD 目录 1. 概述 2. 功能说明 2.1 程序控制 2.2 状态监控 2.3 报警与故障处理 2.4 驱动控制 3. 关键参数说明 4. 操作步骤指南 4.1 初始化配置 4.2 运行控制 4.3 状态监控 5. 常见故障处理 6. 注意事项 附录1:程序段索引 附录…...
linux中如何修改文件的权限和拥有者所属组
目录标题 chmod指令八进制形式权限修改文件拥有者所属组的修改umask有关内容 chmod指令 chmod指令可以用来修改人员的权限其形式如下: u代表的是拥有者,g代表的是所属组,o代表的是其他人,a表示所有人,如果你想增加权…...
掌握Linux项目自动化构建:从零入门make与Makefile
文章目录 前言: 一、初识自动化构建工具1.1 什么是make/Makefile?1.2 快速体验 二、深入理解核心机制2.1 依赖关系与依赖方法2.2 伪目标的妙用2.3 具体语法a.makefile的基本雏形b.makefile推导原则! 三、更加具有通用型的makefile1. 变量定义…...
Jenkins 配置python项目和allure
Jenkins新建项目 新建ry-api-auto-test。 添加项目描述,选择gitee令牌。 源码管理,设置仓库地址和凭证。参考我上一篇文章的链接:配置gitee私人令牌和凭证 构建步骤,因为我Jenkins部署在Windows,因此选择batch。…...
优化 Docker 镜像 技巧
优化 Docker 镜像可以提高构建速度、减少镜像大小、提高安全性和效率。以下是一些优化 Docker 镜像的方法: 使用适当的基础镜像 选择合适的基础镜像可以减小镜像大小,并确保基础镜像的安全性和更新性。Alpine、Ubuntu Minimal 等轻量级基础镜像是常用选…...
从简单场景认识建造者模式
建造者设计模式总的来说常见的形式无非就两种。 一种是具体产物样式多,故通过中间者(指挥者)来统筹决定产生哪种对象(组装电脑,都是电脑,只是参数配置不同)。 一种是构造的可选参数多…...
Maven工具学习使用(四)——仓库
仓库分类 对于Mavne来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标查询寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件之后…...
vue3:十一、主页面布局(进入指定菜单页面,默认锁定到左侧菜单)
一、效果 直接进入home页面,直接展开对应的菜单项 二、具体实现 1、菜单容器增加默认选中变量 在菜单容器中将默认展开菜单default-openeds修改为默认选中菜单default-active 2、引入useRoute方法 引入该方法为了获取当前页面的路径 import { useRoute } from …...
linux,防火墙,firewall,常用命令
文章目录 1. 查看防火墙状态2. 查看当前开放的端口和服务查看所有开放的端口查看所有允许的服务查看所有区域的详细信息 3. 开放指定端口开放端口(临时生效)开放端口(永久生效)开放指定端口范围 4. 删除指定端口删除端口ÿ…...
SQL 函数
SQL 函数 概述 SQL 函数是数据库查询语言(Structured Query Language)的核心组成部分之一。它们是用于执行特定任务的预定义过程,可以在查询中使用以增强查询的灵活性和功能性。SQL 函数可以分为两大类:内置函数和用户自定义函数…...
【蓝桥杯】每日练习 Day13
前言 今天做了不少题,但是感觉都太水了,深思熟虑之下主播决定拿出两道相对不那么水的题来说一下(其实还是很水)。 两道问题,一道是日期问题(模拟),一道是区间合并问题。 日期差值 …...
【Docker系列七】Docker Compose 命令详解
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
【AI学习】Transformer 模型
1,概念 是一种基于自注意力机制(Self-Attention Mechanism)的深度学习架构,在自然语言处理、计算机视觉等多个领域都有着极为重要的应用。 2,基本结构 1)编码器(Encoder) 通常由多个相同的编码器层堆叠而成。 每个编码器层包含了多头自注意力机制、前馈神经网络以及…...
大数据学习栈记——HBase操作(shell java)
本文介绍HBase在shell终端的常见操作以及如何利用java api操作HBase,操作系统:Ubuntu24.04 参考: https://blog.51cto.com/u_16099228/8016429 https://blog.csdn.net/m0_37739193/article/details/73618899 https://cloud.tencent.com/d…...
