第12章:优化并发_《C++性能优化指南》notes

优化并发

      • 一、并发基础与优化核心知识点
      • 二、关键代码示例与测试
      • 三、关键优化策略总结
      • 四、性能测试方法论
      • 多选题
      • 设计题
      • 答案与详解
        • 多选题答案:
      • 设计题答案示例

一、并发基础与优化核心知识点

  1. 线程 vs 异步任务
  • 核心区别std::thread直接管理线程,std::async由运行时决定异步策略(可能用线程池)。
  • 优化点:频繁创建线程开销大,优先用 std::async
  1. 原子操作与内存序
  • 原子类型std::atomic<T>确保操作不可分割。
  • 内存序memory_order_relaxed(无同步)到 memory_order_seq_cst(全序同步)。
  1. 锁的精细控制
  • 锁类型std::mutexstd::recursive_mutexstd::shared_mutex
  • 优化技巧:缩短临界区、避免嵌套锁、用 std::lock_guard/std::unique_lock自动管理。
  1. 条件变量与生产者-消费者
  • 使用模式wait()搭配谓词防止虚假唤醒,notify_one()/notify_all()通知。
  1. 无锁数据结构
  • 适用场景:高并发计数器、队列等,减少锁竞争。

二、关键代码示例与测试

示例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

结论:无锁队列减少锁争用,适合高并发场景。


三、关键优化策略总结

  1. 减少锁竞争

    • 缩小临界区范围。
    • 使用读写锁(std::shared_mutex)区分读写操作。
  2. 利用原子操作

    • 简单计数器优先用 std::atomic
    • 适当选择内存序(如 memory_order_relaxed)。
  3. 异步与线程池

    • 避免频繁创建线程,使用 std::async或自定义线程池。
  4. 无锁数据结构

    • 在CAS(Compare-And-Swap)安全时使用,但需注意ABA问题。
  5. 缓存友好设计

    • 避免false sharing(用 alignas对齐或填充字节)。

四、性能测试方法论

  1. 基准测试

    • 使用 std::chrono精确测量代码段耗时。
    • 对比不同实现的吞吐量(如ops/sec)。
  2. 压力测试

    • 模拟高并发场景,观察资源竞争情况。
  3. 工具辅助

    • Valgrind检测内存问题。
    • perf分析CPU缓存命中率。

多选题

  1. 下列哪些情况可能导致数据竞争?
    A. 多个线程同时读取同一变量
    B. 一个线程写,另一个线程读同一变量
    C. 使用互斥量保护共享变量
    D. 使用原子变量操作

  2. 关于std::asyncstd::thread的选择,正确的说法是?
    A. std::async默认启动策略是延迟执行
    B. std::thread更适合需要直接控制线程生命周期的场景
    C. std::async会自动管理线程池
    D. std::thread无法返回计算结果

  3. 以下哪些操作可能引发死锁?
    A. 在持有锁时调用外部未知代码
    B. 对多个互斥量使用固定顺序加锁
    C. 递归互斥量(std::recursive_mutex)的嵌套加锁
    D. 未及时释放条件变量关联的锁

  4. 关于原子操作的内存顺序,正确的说法是?
    A. memory_order_relaxed不保证操作顺序
    B. memory_order_seq_cst会降低性能但保证全局顺序
    C. memory_order_acquire仅保证读操作的可见性
    D. 原子操作必须与volatile关键字结合使用

  5. 优化同步的合理手段包括:
    A. 将大临界区拆分为多个小临界区
    B. 使用无锁数据结构替代互斥量
    C. 通过std::future传递计算结果
    D. 在单核系统上使用忙等待(busy-wait)

  6. 关于条件变量的正确使用方式:
    A. 必须在循环中检查谓词
    B. notify_one()notify_all()更高效
    C. 可以脱离互斥量单独使用
    D. 虚假唤醒(spurious wakeup)是必须处理的

  7. 以下哪些是"锁护送"(Lock Convoy)的表现?
    A. 多个线程频繁争抢同一互斥量
    B. 线程因锁竞争频繁切换上下文
    C. 互斥量的持有时间极短
    D. 使用读写锁(std::shared_mutex

  8. 减少共享数据竞争的方法包括:
    A. 使用线程局部存储(TLS)
    B. 复制数据到各线程独立处理
    C. 通过消息队列传递数据
    D. 增加互斥量的数量

  9. 关于std::promisestd::future的正确说法是:
    A. std::promise只能设置一次值
    B. std::futureget()会阻塞直到结果就绪
    C. 多个线程可以共享同一个std::future对象
    D. std::async返回的std::future可能延迟执行

  10. 关于原子变量与互斥量的对比,正确的说法是:
    A. 原子变量适用于简单数据类型
    B. 互斥量能保护复杂操作序列
    C. 原子变量的fetch_add是原子的
    D. 互斥量比原子变量性能更好


设计题

  1. 实现一个线程安全的无锁(lock-free)队列
    要求:
  • 使用原子操作实现pushpop
  • 处理ABA问题
  • 提供测试代码验证并发操作正确性
  1. 设计一个生产者-消费者模型
    要求:
  • 使用std::condition_variablestd::mutex
  • 队列长度限制为固定大小
  • 支持多生产者和多消费者
  • 提供测试代码模拟并发场景
  1. 实现基于std::async的并行任务执行器
    要求:
  • 支持提交任意可调用对象
  • 自动回收已完成的任务资源
  • 限制最大并发线程数为CPU核心数
  • 测试代码展示并行加速效果
  1. 优化高竞争场景下的计数器
    要求:
  • 使用线程局部存储(TLS)分散写操作
  • 定期合并局部计数到全局变量
  • 对比普通原子计数器与优化版本的性能差异
  • 提供测试代码和性能统计输出

5 实现一个读写锁(Read-Write Lock)
要求:

  • 支持多个读者或单个写者
  • 避免写者饥饿(writer starvation)
  • 基于std::mutexstd::condition_variable实现
  • 测试代码验证读写操作的互斥性

答案与详解


多选题答案:
  1. B
    解析:数据竞争的条件是至少一个线程写且无同步措施。A只有读不冲突,C/D有同步机制。

  2. B, D
    解析std::async默认策略非延迟(C++11起为std::launch::async|deferred),B正确,D因std::thread无直接返回值机制正确。

  3. A, C
    解析:A可能导致回调中再次加锁;C递归锁允许同一线程重复加锁但需对应解锁次数,误用仍可能死锁。

  4. A, B
    解析memory_order_relaxed无顺序保证,B正确,C中acquire保证后续读的可见性,D错误(原子操作无需volatile)。

  5. A, B, C
    解析:D在单核忙等待浪费CPU,其余均为有效优化手段。

  6. A, B, D
    解析:C错误,条件变量必须与互斥量配合使用。

  7. A, B
    解析:锁护送表现为频繁争抢导致线程切换,C短持有时间反而减少竞争,D无关。

  8. A, B, C
    解析:D增加锁数量可能加剧竞争,其余均为减少竞争的有效方法。

  9. A, B, D
    解析:C错误,std::future不可多线程同时调用get()

  10. A, B, C
    解析:D错误,互斥量在低竞争时性能可能更差。


设计题答案示例

  1. 无锁队列实现(部分代码)
#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;
}
  1. 生产者-消费者模型
#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;
}

其他设计题目的答案, 稍后补充

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

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

相关文章

深入理解椭圆曲线密码学(ECC)与区块链加密

椭圆曲线密码学&#xff08;ECC&#xff09;在现代加密技术中扮演着至关重要的角色&#xff0c;广泛应用于区块链、数字货币、数字签名等领域。由于其在提供高安全性和高效率上的优势&#xff0c;椭圆曲线密码学成为了数字加密的核心技术之一。本文将详细介绍椭圆曲线的基本原理…

SQL Server 2008安装教程

目录 一.安装SQL Server 二.安装SQL Server Management Studio 三.使用SQL Server Management Studio 一.安装SQL Server 官网下载:SQL Server 下载 | Microsoft 1.选择安装中的全新安装如下图 2.功能选择 3.实例配置 4.后面一直下一步到数据库引擎配置 密码自己设置 系统…

Microi吾码界面设计引擎之基础组件用法大全【内置组件篇·中】

&#x1f380;&#x1f380;&#x1f380; microi-pageengine 界面引擎系列 &#x1f380;&#x1f380;&#x1f380; 一、Microi吾码&#xff1a;一款高效、灵活的低代码开发开源框架【低代码框架】 二、Vue3项目快速集成界面引擎 三、Vue3 界面设计插件 microi-pageengine …

如何在 Windows 上安装并使用 Postman?

Postman 是一个功能强大的API测试工具&#xff0c;它可以帮助程序员更轻松地测试和调试 API。在本文中&#xff0c;我们将讨论如何在 Windows 上安装和使用 Postman。 Windows 如何安装和使用 Postman 教程&#xff1f;

便携版:随时随地,高效处理 PDF 文件

PDF-XChange Editor Plus 便携版是一款功能强大且极其实用的 PDF 阅读与编辑工具。它不仅支持快速浏览 PDF 文件&#xff0c;还提供了丰富的编辑功能&#xff0c;让用户可以轻松处理 PDF 文档。经过大神优化处理&#xff0c;这款软件已经变得十分轻便&#xff0c;非常适合需要随…

MCP Server 实现一个 天气查询

​ Step1. 环境配置 安装 uv curl -LsSf https://astral.sh/uv/install.sh | shQuestion: 什么是 uv 呢和 conda 比有什么区别&#xff1f; Answer: 一个用 Rust 编写的超快速 (100x) Python 包管理器和环境管理工具&#xff0c;由 Astral 开发。定位为 pip 和 venv 的替代品…

MySQL执行计划

MySQL 的 执行计划&#xff08;Execution Plan&#xff09; 是优化器根据 SQL 语句生成的查询执行路径的详细说明。通过分析执行计划&#xff0c;可以了解 MySQL 如何处理 SQL 查询&#xff08;如索引使用情况、表连接顺序等&#xff09;&#xff0c;进而优化查询性能。 1. 获…

数据大屏点亮工业互联网的智慧之眼

在当今数字化飞速发展的时代&#xff0c;数据已成为企业决策的核心依据&#xff0c;而数据大屏作为数据可视化的重要工具&#xff0c;正逐渐成为工业互联网领域不可或缺的一部分。通过直观、动态的可视化展示&#xff0c;数据大屏能够将复杂的数据转化为易于理解的图表和图形&a…

GPT-SoVITS本地部署:低成本实现语音克隆远程生成音频全流程实战

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 今天要给大家安利一个绝对能让你大呼过瘾的声音黑科技——GPT-SoVITS&#xff01;这款由花儿不哭大佬精心打造的语音克隆神…

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

目录 一、前言 二、AI视频概述 2.1 什么是AI视频 2.2 AI视频核心特点 2.3 AI视频应用场景 三、通义万相介绍 3.1 通义万相概述 3.1.1 什么是通义万相 3.2 通义万相核心特点 3.3 通义万相技术特点 3.4 通义万相应用场景 四、DeepSeek 通义万相制作AI视频流程 4.1 D…

【Unity】合批处理和GPU实例化的底层优化原理(完)

【Unity】批处理和实例化的底层优化原理 URP1.基础概念SetPassCallsDrawCallsBatches 2.重要性排序既然如此为什么仍然要合批&#xff1f; 3.unity主流的合批优化方案和优先级Early-Z透明物体情况 4.合批&#xff08;小场景但是很复杂很多小物件刚需&#xff09;合并纹理图集更…

当人类关系重构:从“相互需要”到“鹅卵石化”——生成式人工智能(GAI)认证的角色与影响

在数字化浪潮的席卷之下,人类社会正经历着前所未有的变革。人与人之间的连接方式、互动模式以及价值认同,都在悄然发生着变化。这一过程中,一个显著的现象是,人与人之间的关系逐渐从传统的“相互需要”模式,转变为一种更为复杂、多元且稳定的“鹅卵石化”结构。在此背景下…

ctfhow——web入门171~175

sql简介 web入门171 ——判断注入点&#xff1a; -1 union select 1,2,3 -- 其实在这之前可以先判断多少列&#xff0c;即 -1‘ group&#xff08;order&#xff09; by 3 -- group by用于将具有相同值的行分组成一个汇总行&#xff0c;可以查看是否报错确定列数 2&#x…

vue遗漏的知识点(动态组件.组件库的操作使用)

----动态组件&#xff08;vue2vue3通用&#xff09; <component :is"..."> 的作用 <component> 是 Vue 的内置组件&#xff0c;用于动态渲染其他组件。:is 属性 用于指定要渲染的组件。它的值可以是&#xff1a; 组件的名称&#xff08;字符串&#xf…

ip改变导致的数据库连接不上

前言 需要用到路由器&#xff0c;所以先把家里的路由器给拆了先用着。新的路由器到了之后&#xff0c;更换上新的路由器之后&#xff0c;调用到服务会有报错&#xff0c;记录一下更换路由器之后ip重新分配服务可能会报的错. 进一步可以看到有关网路在服务当中的影响。 正文 …

DeepSeek面试——模型架构和主要创新点

本文将介绍DeepSeek的模型架构多头潜在注意力&#xff08;MLA&#xff09;技术&#xff0c;混合专家&#xff08;MoE&#xff09;架构&#xff0c; 无辅助损失负载均衡技术&#xff0c;多Token 预测&#xff08;MTP&#xff09;策略。 一、模型架构 DeepSeek-R1的基本架构沿用…

基于HTML5的3D魔方项目开发实践

基于HTML5的3D魔方项目开发实践 这里写目录标题 基于HTML5的3D魔方项目开发实践项目概述核心技术实现1. 3D效果实现CSS3 3D变换魔方结构设计 2. 交互控制实现动画控制键盘控制触摸控制 技术难点与解决方案1. 3D变换控制2. 触摸体验优化3. 动画性能优化 项目收获总结项目展望 项…

23种设计模式-原型(Prototype)设计模式

原型设计模式 &#x1f6a9;什么是原型设计模式&#xff1f;&#x1f6a9;原型设计模式的特点&#x1f6a9;原型设计模式的结构&#x1f6a9;原型设计模式的优缺点&#x1f6a9;原型设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是原型设计模式…

【MATLAB例程】交互式多模型(IMM),模型使用:CV,CT左转、CT右转,二维平面,三个模型的IMM,滤波使用EKF。订阅专栏后可查看代码

简单的介绍:本文所述的代码实现了一种基于交互多模型(IMM)算法的目标跟踪仿真,适用于复杂运动目标(如匀速、转弯运动)的状态估计。代码通过三个运动模型(匀速CV、左转弯CT1、右转弯CT2)的协同滤波,动态调整模型概率,最终输出综合跟踪结果。代码包含完整的仿真数据生成…

搭建私人对外git空间

# 创建用户&#xff0c;指定不可登录的 Shell&#xff08;git-shell 或 /usr/sbin/nologin&#xff09; sudo adduser --system --shell /usr/bin/git-shell --group git # 验证用户配置 grep git /etc/passwd # 预期输出&#xff1a;git:x:998:998::/home/git:/usr/bin/git-s…