C++核心指导原则: 并发和并行

embedded/2025/2/28 1:52:34/

C++ Core Guidelines 整理目录

  1. 哲学部分
  2. 接口(Interface)部分
  3. 函数部分
  4. 类和类层次结构部分
  5. 枚举部分
  6. 资源管理部分
  7. 表达式和语句部分
  8. 性能部分
  9. 并发和并行
  10. 错误处理

一般规则

CP.1: Assume that your code will run as part of a multi-threaded program
  • 解释: 假设你的代码将会作为多线程程序的一部分运行.
  • 原因: 即使当前代码看起来是单线程的, 未来的扩展或集成到更大的系统中可能需要并发执行. 提前考虑并发问题可以在设计上留有余地.
CP.2: Avoid data races
  • 解释: 避免数据竞争, 确保对共享数据的访问是同步的.
  • 原因: 数据竞争会导致未定义行为, 包括程序崩溃, 不正确的结果等. 正确地管理共享资源可以提高程序的稳定性和可靠性.
CP.3: Minimize explicit sharing of writable data
  • 解释: 尽量减少显式共享可写数据.
  • 原因: 减少直接共享数据可以降低数据竞争的风险, 并简化并发控制逻辑. 这有助于提升代码的可维护性和性能.
CP.4: Think in terms of tasks, rather than threads
  • 解释: 应该以任务而非线程来思考.
  • 原因: 以任务为导向的设计使得开发者更容易关注于要完成的工作而不是底层的执行机制. 这种方法提高了资源利用效率, 简化了并行编程.
CP.8: Don’t try to use volatile for synchronization
  • 解释: 不要尝试使用volatile来进行同步操作.
  • 原因: volatile关键字不能保证原子性或互斥, 它仅用于防止编译器优化掉对变量的读写操作. 应使用适当的同步原语(如互斥锁)来确保线程安全.
CP.9: Whenever feasible use tools to validate your concurrent code
  • 解释: 尽可能使用工具验证你的并发代码.
  • 原因: 并发错误往往难以复现和调试. 利用静态分析工具, 动态分析工具等可以帮助检测死锁, 竞态条件等问题, 从而提高代码质量并减少潜在的错误. Thread Sanitizer(TSAN)可以帮助检测并发错误.

并发规则

CP.20: Use RAII, never plain lock()/unlock()
  • 解释: 使用资源获取即初始化(RAII)机制来管理锁, 而不是直接调用 lock()unlock().
  • 原因: RAII 机制可以确保锁的释放, 从而避免死锁和竞争条件. 锁相关的主题, 可以参考我的这篇博客: [现代 C++锁介绍]({{<ref “/posts/2024-12-24-modern-cpp-lock-intro.md” >}})
CP.21: Use std::lock() or std::scoped_lock to acquire multiple mutexes
  • 解释: 使用std::lock()std::scoped_lock来获取多个互斥锁.
  • 原因: 这些机制可以避免死锁问题, 因为它们保证了锁的顺序一致性和自动释放. 锁相关的主题, 可以参考我的这篇博客: [现代 C++锁介绍]({{<ref “/posts/2024-12-24-modern-cpp-lock-intro.md” >}})
CP.22: Never call unknown code while holding a lock (e.g., a callback)
  • 解释: 持有锁时不要调用未知代码(例如回调函数).
  • 原因: 未知代码可能执行长时间操作或尝试获取其他锁, 导致潜在的死锁或性能问题.
CP.23: Think of a joining thread as a scoped container
  • 解释: 将加入线程视为作用域容器.
  • 原因: 这种思维方式确保线程在其生命周期内正确管理资源, 并在作用域结束时自动清理.
CP.24: Think of a thread as a global container
  • 解释: 将线程视为全局容器.
  • 原因: 提醒开发者注意线程之间共享数据的安全性, 确保对共享资源进行适当的同步.
CP.25: Prefer gsl::joining_thread over std::thread
  • 解释: 优先使用gsl::joining_thread而不是std::thread.
  • 原因: gsl::joining_thread提供了更安全的接口, 默认情况下会在析构时等待线程完成, 减少资源泄漏的风险. C++20 有了std::jthread, 可以使用std::jthread来替换gsl::joining_thread. 关于 jthread, 可以参考我的这篇博客: [C++20 中的 jthread: 从 RAII 到线程管理的进化]({{<ref “/posts/2024-08-01-cpp20-jthread.md” >}})
CP.26: Don’t detach() a thread
  • 解释: 不要分离线程.
  • 原因: 分离线程可能导致资源无法正确回收, 增加内存泄漏和其他资源管理问题的风险.
CP.31: Pass small amounts of data between threads by value, rather than by reference or pointer
  • 解释: 在线程间通过值传递少量数据, 而不是引用或指针.
  • 原因: 值传递避免了共享可变状态带来的复杂性和潜在的数据竞争问题.
CP.32: To share ownership between unrelated threads use shared_ptr
  • 解释: 在不相关的线程之间共享所有权时使用shared_ptr.
  • 原因: shared_ptr提供了线程安全的所有权管理和引用计数, 减少了手动管理资源的复杂性.
CP.40: Minimize context switching
  • 解释: 尽量减少上下文切换.
  • 原因: 上下文切换会带来额外的开销, 影响系统性能. 合理设计任务调度可以减少不必要的切换.
CP.41: Minimize thread creation and destruction
  • 解释: 尽量减少线程的创建和销毁.
  • 原因: 线程的创建和销毁是昂贵的操作, 建议使用线程池等技术来重用线程, 提高效率.
CP.42: Don’t wait without a condition
  • 解释: 不要在没有条件的情况下等待.
  • 原因: 无条件等待会导致线程长时间阻塞, 浪费资源. 应确保等待是有条件且合理的.
CP.43: Minimize time spent in a critical section
  • 解释: 尽量减少在临界区花费的时间.
  • 原因: 长时间占用锁会增加其他线程等待的时间, 降低并发性能.
CP.44: Remember to name your lock_guards and unique_locks
  • 解释: 记得为你的lock_guardunique_lock命名.
  • 原因: 明确命名有助于代码的可读性和维护性, 便于理解锁定的意图和范围.
CP.50: Define a mutex together with the data it guards. Use synchronized_value<T> where possible
  • 解释: 定义互斥锁时一并定义它保护的数据. 尽可能使用synchronized_value<T>.
  • 原因: 这种封装方式提高了代码的安全性和可读性, 确保数据访问的一致性和同步性.

协程规则

CP.51: Do not use capturing lambdas that are coroutines
  • 解释: 不要使用捕获协程的 lambda 表达式.
  • 原因: 捕获协程的 lambda 可能会导致复杂的生命周期问题, 并且难以管理资源和状态.
CP.52: Do not hold locks or other synchronization primitives across suspension points
  • 解释: 不要在挂起点持有锁或其他同步原语.
  • 原因: 在挂起点持有锁会导致潜在的死锁问题, 并且增加其他线程等待的时间, 降低并发性能.
CP.53: Parameters to coroutines should not be passed by reference
  • 解释: 协程的参数不应通过引用传递.
  • 原因: 引用传递可能导致悬空指针或未定义行为, 尤其是在协程挂起和恢复的过程中. 值传递更加安全和明确.

消息传递规则

CP.60: Use a future to return a value from a concurrent task
  • 解释: 使用future从并发任务中返回值.
  • 原因: future提供了一种简单且线程安全的方式, 允许异步操作的结果在稍后被检索. 关于 future, 可以参考我的这篇博客: [C++异步任务: std::async, std::future 和 std::promise]({{<ref “/posts/2024-12-31-cpp-future-and-promise.md” >}}).
CP.61: Use async() to spawn concurrent tasks
  • 解释: 使用async()来启动并发任务.
  • 原因: async()简化了并发任务的创建和管理, 提供了自动化的线程池管理和结果获取机制.

无锁编程规则

CP.100: Don’t use lock-free programming unless you absolutely have to
  • 解释: 不要使用无锁编程, 除非绝对必要.
  • 原因: 无锁编程复杂且容易出错, 通常需要深入理解底层硬件和内存模型. 只有在性能要求极高且无法通过其他方式满足时才考虑使用.
CP.101: Distrust your hardware/compiler combination
  • 解释: 对你的硬件/编译器组合保持怀疑态度.
  • 原因: 不同硬件和编译器对无锁编程的支持和实现可能不同, 这可能导致不可预见的行为和错误. 仔细验证代码在目标平台上的正确性.
CP.102: Carefully study the literature
  • 解释: 仔细研究相关文献.
  • 原因: 无锁编程涉及复杂的概念和技术, 深入学习现有的研究成果和最佳实践有助于避免常见陷阱和错误.
CP.110: Do not write your own double-checked locking for initialization
  • 解释: 不要自己编写双重检查锁定用于初始化.
  • 原因: 双重检查锁定模式虽然看似简单, 但在多线程环境中容易出错, 特别是在处理内存顺序和可见性问题时. 应使用经过验证的标准库或模式.
CP.111: Use a conventional pattern if you really need double-checked locking
  • 解释: 如果确实需要双重检查锁定, 请使用标准模式.
  • 原因: 标准模式(如 C++11 中的std::call_once)经过充分测试和验证, 能够正确处理并发初始化问题, 避免手动实现带来的复杂性和风险.

其他规则

CP.200: Use volatile only to talk to non-C++ memory
  • 解释: 仅在与非 C++内存进行通信时使用volatile.
  • 原因: volatile关键字用于指示编译器不要优化对变量的访问, 但通常仅在与非 C++内存进行通信时使用.

http://www.ppmy.cn/embedded/167688.html

相关文章

[算法--前缀和] 矩阵区域和

目录 1. 二维前缀和的知识铺垫2. 以nums[i][j]为中心计算区域大小.3. dp数组与ret数组之间的逻辑关系.4. 细节: 如果[i,j]为中心的数组越界了呢?下面继续分享一道用前缀和思想解决的算法问题 -> 矩阵区域和 1. 二维前缀和的知识铺垫 实际上, 有一道十分类似的基础题 ->…

HTML篇

1. src和href的区别 &#xff08;1&#xff09;src src 是 source 的缩写&#xff0c;指向外部资源的位置&#xff0c;指向的内容将会嵌入到文档中当前标签所在位置&#xff1b;在请求 src 资源时会将其指向的资源下载并应用到文档内&#xff0c;例如 js 脚本&#xff0c;img …

使用 Containerd 通过 HTTP 协议拉取 Harbor 私有镜像仓库的镜像

在 Kubernetes 1.24及以上版本环境中&#xff0c;docker不再被支持&#xff0c;主要使用Containerd 是常用的容器运行。默认情况下&#xff0c;Containerd 使用 HTTPS 协议与镜像仓库通信。然而&#xff0c;在某些场景下&#xff08;如测试环境或内部网络&#xff09;&#xff…

TCP协议中TIME_WAIT状态的分析

在计算机网络中&#xff0c;TCP&#xff08;传输控制协议&#xff09;是一种重要的协议&#xff0c;它提供可靠的、面向连接的通信。TCP协议通过一个复杂的状态机管理连接的生命周期&#xff0c;其中 TIME_WAIT状态是其核心机制之一。理解 TIME_WAIT状态对于深入了解TCP协议的运…

C进阶 自定义类型

目录 前言 一 结构体 二 结构体的存储 三 位段 四 枚举 五 联合体 总结 前言 我们之前学习的int char double ......都是内置类型&#xff0c;但是我们今天所学习的是自定义类型&#xff0c;比如联合体&#xff0c;结构体&#xff0c;枚举 一 结构体 结构体是一…

信创终端上如何将PDF文件转为OFD文件

原文链接&#xff1a;信创终端上如何将PDF文件转为OFD文件 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在信创终端上使用永中OFD板式软件、福昕OFD板式办公套件、点聚OFD板式软件、友虹OFD3.0将PDF转换为OFD文件的文章。在信创环境下&#xff0c;OFD作为国产…

VScode 开发

目录 安装 VS Code 创建一个 Python 代码文件 安装 VS Code VSCode&#xff08;全称&#xff1a;Visual Studio Code&#xff09;是一款由微软开发且跨平台的免费源代码编辑器&#xff0c;VSCode 开发环境非常简单易用。 VSCode 安装也很简单&#xff0c;打开官网 Visual S…

Redis 缓存穿透、击穿、雪崩:问题与解决方案

在使用 Redis 作为缓存中间件时&#xff0c;系统可能会面临一些常见的问题&#xff0c;如 缓存穿透、缓存击穿 和 缓存雪崩。这些问题如果不加以解决&#xff0c;可能会导致数据库压力过大、系统响应变慢甚至崩溃。本文将详细分析这三种问题的起因&#xff0c;并提供有效的解决…