C++ 多线程与并发系统取向(七)—— 并发排障与工程纪律(从“会写”到“能控场”)

一、并发问题的三大类

所有并发 bug,本质都落在三类:

类型 症状 本质
数据竞争 结果偶现错误 未同步写
死锁 程序卡死 锁循环等待
性能退化 CPU 高 / 卡顿 锁竞争严重

你必须学会区分它们。

二、数据竞争(Race Condition)

典型症状:

  • 本地跑正常
  • Release 才出问题
  • 多核机器更容易复现
  • 偶现 bug

示例

cpp 复制代码
if (counter > 0) {
    counter--;
}

即使 counter 是 atomic,也不安全。

因为:

判断 + 修改 不是原子事务。

如何定位?

1️⃣ 检查是否有共享变量

2️⃣ 是否存在写操作

3️⃣ 是否所有访问都在锁内

工程纪律:

所有共享变量必须有"唯一保护策略"。

不要:

  • 一部分用锁
  • 一部分不用锁
  • 一部分用 atomic

必须统一。

三、死锁(Deadlock)

死锁的四个必要条件:

1️⃣ 互斥

2️⃣ 持有并等待

3️⃣ 不可抢占

4️⃣ 循环等待

只要破坏一个条件,就不会死锁。

经典死锁例子

cpp 复制代码
// 线程 A
lock(m1);
lock(m2);

// 线程 B
lock(m2);
lock(m1);

解决方案 1:统一锁顺序

永远按顺序:

cpp 复制代码
lock(m1);
lock(m2);

解决方案 2:std::lock

cpp 复制代码
std::unique_lock<std::mutex> lock1(m1, std::defer_lock);
std::unique_lock<std::mutex> lock2(m2, std::defer_lock);
std::lock(lock1, lock2);

避免循环等待。

四、锁竞争(性能问题)

症状:

  • CPU 高
  • 线程多但吞吐不涨
  • perf 显示大量等待

锁太大

cpp 复制代码
std::lock_guard lock(mtx);
doHeavyTask();

错误。

正确方式

cpp 复制代码
{
    std::lock_guard lock(mtx);
    popTask();
}
doHeavyTask();

工程优化策略

1️⃣ 缩小临界区

2️⃣ 分段锁(多个 mutex)

3️⃣ 读写锁(shared_mutex)

4️⃣ 避免过度 atomic 自旋

五、条件变量常见坑

❌ 用 if 而不是 while

必须:

cpp 复制代码
cv.wait(lock, condition);

❌ 在锁外修改状态

状态必须在锁内更新。

❌ 忘记 notify

所有改变条件的操作必须通知。

六、atomic 的常见误用

误区 1:用 atomic 保护容器

复制代码
std::atomic<int> size;

size 正确,不代表 vector 安全。

误区 2:用 atomic 替代锁

atomic 不能做事务。

七、线程生命周期问题

1️⃣ 忘记 join

程序崩溃。

2️⃣ detach 滥用

线程失控。

原则:

所有线程必须有明确 owner。

八、并发调试方法论

1️⃣ 先确认问题类型

卡死?

数据错?

性能慢?

2️⃣ 打印线程 id

cpp 复制代码
std::this_thread::get_id();

3️⃣ 加日志定位锁持有时间

4️⃣ 使用工具(进阶)

  • ThreadSanitizer
  • valgrind
  • perf
  • gdb bt

九、工程级并发纪律清单(必须收藏)

资源层

✅ 每个共享资源有唯一 mutex

✅ 不允许"有时加锁,有时不加锁"


等待层

✅ wait 必须带 predicate

✅ 状态必须锁内修改

✅ 修改后必须 notify


状态层

✅ atomic 只用于简单状态

✅ 不做复杂逻辑


生命周期

✅ 所有线程必须 join

✅ shutdown 必须可控

十、Java 对比总结

Java:

  • 有 GC
  • 有成熟线程池
  • 有 JVM 优化

C++:

  • 更底层
  • 更危险
  • 更可控

你写的每一行锁:

都是 JVM 在 Java 里帮你干的事情。

十一、并发系统最终模型(终章脑图)

你现在脑子里应该是这样:

cpp 复制代码
线程模型
   ↓
共享资源
   ↓
mutex 保护
   ↓
condition_variable 协作
   ↓
atomic 状态控制
   ↓
生命周期管理
   ↓
排障与优化

这就是:

并发系统取向思维。

十二、全系列总结一句话

线程私有栈,共享堆

共享必保护

保护用 mutex

协作用 cv

状态用 atomic

生命周期可控

锁只保护资源,不保护业务

十三、你现在是什么水平?

如果你完全理解这 7 篇:

你已经:

✔ 不再是"会写 thread 的人"

✔ 而是具备"并发系统思维的人"

这已经是:

C++ 工程中级偏上水平的并发基础。

相关推荐
载数而行52012 小时前
QT的五类布局
c++·qt·学习
Cg1362691597412 小时前
JS-对象-Dom案例
开发语言·前端·javascript
故事和你9112 小时前
sdut-程序设计基础Ⅰ-实验五一维数组(8-13)
开发语言·数据结构·c++·算法·蓝桥杯·图论·类和对象
载数而行52012 小时前
QT的QString类
c++·qt·学习
Jin、yz13 小时前
JAVA 八股
java·开发语言
我是唐青枫13 小时前
C#.NET Span 深入解析:零拷贝内存切片与高性能实战
开发语言·c#·.net
lxh011313 小时前
数据流的中位数
开发语言·前端·javascript
bu_shuo13 小时前
Visual C++2010学习版(全国计算机等级二级考试版)安装记录
c++·cpp·visual c++·计算机二级
盒马盒马13 小时前
Rust:迭代器
开发语言·后端·rust
Full Stack Developme14 小时前
Java 常用通信协议及对应的框架
java·开发语言