C++ Concurrency in Action (第2版) 笔记

原始文件: /books/c++/C++ Concurrency in Action 2nd.pdf
大小: 5.72MB
页数: 592页
作者: Anthony Williams
出版: Manning, 2019
格式: 文字版PDF


核心观点

本书是C++并发编程的权威指南,由C++标准委员会成员、Boost.Thread库原开发者编写。覆盖C++11/14/17的并发设施,从基础线程管理到高级无锁数据结构设计。

关键概念

1. 并发 vs 并行

  • 并发 (Concurrency): 多个任务同时进展(逻辑上的同时)
  • 并行 (Parallelism): 多个任务同时执行(物理上的同时)
  • 并发可用于分离关注点(如UI响应+后台计算),并行主要用于性能提升

2. C++并发支持演进

  • C++11: 首次引入标准线程库(std::thread, std::mutex, std::atomic等)
  • C++14/17: 增强(std::shared_mutex, std::string_view, parallel algorithms)
  • Concurrency TS: 扩展提案(latch, barrier, when_all/when_any)

3. 内存模型基础

  • objects and memory locations: 对象与内存位置的区别
  • modification orders: 修改顺序
  • happens-before relationship: 先行发生关系
  • synchronizes-with: 同步关系

4. 原子操作与内存序

  • std::atomic_flag: 最简单的原子类型
  • std::atomic: 通用原子类型
  • 内存序选项:
    • seq_cst (默认, 最强一致性)
    • acquire/release (平衡性能与正确性)
    • relaxed (最弱,仅保证原子性)
    • consume (特定场景)

5. 同步原语

  • Mutexes: std::mutex, std::recursive_mutex, std::shared_mutex, std::timed_mutex
  • Locks: std::lock_guard (RAII), std::unique_lock (灵活锁定)
  • Condition Variables: std::condition_variable, std::condition_variable_any
  • Futures/Promises: 异步结果传递
  • std::async: 简化异步任务启动

6. 锁基数据结构设计

  • 线程安全栈(基于锁)
  • 线程安全队列(基于锁+条件变量)
  • 细粒度锁队列
  • 查找表、列表的线程安全实现

7. 无锁数据结构设计

  • 定义: lock-free, wait-free, nonblocking
  • 内存回收难题: ABA问题、hazard pointers、引用计数
  • 无锁栈: 使用CAS操作
  • 无锁队列: SPSC(单生产者单消费者)优化
  • Guidelines: 用seq_cst原型,再优化到所需内存序

8. 并发代码设计原则

  • 数据分割: 按数据划分任务
  • 递归分割: divide and conquer
  • 任务类型分割: 不同阶段不同线程
  • 性能因素:
    • CPU数量
    • 数据竞争与缓存ping-pong
    • False sharing(假共享)
    • Oversubscription(超售)
    • 数据局部性

9. 线程池

  • 最简单线程池实现
  • 等待任务完成
  • 任务依赖处理
  • Work stealing(工作窃取)

10. C++17并行算法

  • std::execution策略
    • sequenced_policy
    • parallel_policy
    • parallel_unsequenced_policy
  • std::for_each, std::transform等并行版本

11. 多线程测试与调试

  • Bug类型: 非预期阻塞、竞态条件
  • 定位技术:
    • 代码审查
    • 测试定位
    • 专门库(如TSan)
  • 性能测试: 基准测试方法

可行动点

  1. 新项目: 直接使用std::thread, std::async, std::future替代POSIX线程
  2. 代码审查: 检查是否所有共享数据都有适当保护
  3. 性能优化: 识别并解决false sharing(使用alignas)
  4. 无锁探索: 对于高频访问数据结构,考虑lock-free设计
  5. 现代C++: 善用std::shared_mutex(读写锁)提升读多写少场景性能
  6. 并行算法: 对大数据集操作使用C++17并行算法
  7. 测试策略: 建立多线程测试框架,定期运行race detection工具

与其他知识关联

重要结论

  1. 原子操作是基石: 理解内存序对于正确并发编程至关重要
  2. RAII是朋友: 始终使用lock_guard/unique_lock管理锁
  3. 无锁≠更快: 需要仔细权衡,多数场景锁已足够
  4. 测试是关键: 并发bug难以复现,需系统化测试策略
  5. C++17并行算法: 对现有代码提升性能的低垂果实