其他
并发 Bug 诊断:用 TSan/Helgrind/GDB…
当程序出现数据竞争、死锁或 std::atomic 内存序问题时,用 TSan、Helgrind、GDB 和 happens-before 推理来定位并修复。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 47/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 50/100 ×0.15
适用场景
并发 Bug 诊断:用 TSan/Hel通用
解决思路
- 用 ThreadSanitizer 检测数据竞争:编译时加 -fsanitize=thread -g -O1(clang 或 gcc),运行程序让 TSan 在运行时拦截内存访问;可用 TSAN_OPTIONS="halt_on_error=1:second_deadlock_stack=1" 控制行为。
- 按顺序读 TSan 报告:第 1 行是访问类型(write/read)和地址;"Write of size" 下的栈是执行写操作的线程;"Previous read/write" 下的栈是冲突线程;"Thread T2 created at" 是线程创建位置;据此定位两个函数在同一地址上无同步的访问。
- 按常见竞争模式修复:全局变量无锁读写→加 mutex 或用 std::atomic;无 atomic 的双重检查锁→用 std::once_flag + std::call_once;共享整数 +=→用 std::atomic<int>::fetch_add();迭代时修改容器→锁住整个临界区;shared_ptr 引用计数本身安全(原子),但被指向对象未必安全。
- 用 Helgrind 检测锁序违规与竞争:运行 valgrind --tool=helgrind --log-file=helgrind.log ./prog;报告会显示 "lock order M2 after M1" 的违规顺序及建立该顺序的代码位置。锁序违规即潜在死锁(T1 先 M1 后 M2,T2 先 M2 后 M1),修复方法是全局统一锁顺序,处处都先取 M1 再取 M2。
- 用 GDB 检测死锁:gdb -p $(pgrep prog) 附加到死锁进程;info threads 列出所有线程,阻塞在 __lll_lock_wait 的线程即在等 mutex;对每个线程执行 thread N 和 bt 查看它在等哪个 mutex、持有哪些锁;用 p ((pthread_mutex_t*)0x601090)->__data.__owner 查看 Linux glibc mutex 的持有者 TID;也可用 GDB 7+ 的 Python 脚本遍历所有线程并打印 bt 3。
- 避免 std::atomic 误用:不要写 if (counter == 0) counter = 1; 这种非原子的复合操作(TOCTOU 竞争),应改用 compare_exchange_strong;发布数据时不要用 memory_order_relaxed 存同步标志,生产者用 memory_order_release 存、消费者用 memory_order_acquire 读,才能建立 happens-before;跨线程共享的非原子数据必须用 mutex 或 atomic 保护,否则是 UB。
- 用 happens-before 推理正确性:线程内 sequenced-before(代码顺序);跨线程 synchronizes-with(同一 atomic 变量上的 release store 与 acquire load 配对,store 前的一切 happens-before load 后的一切);线程创建/join(spawn 同步于线程内动作,线程内动作同步于 join);mutex 的 unlock 同步于下一个 lock。
- 在 Rust 中利用编译期保证:共享可变状态用 Arc<Mutex<T>>,用 Arc::clone 后 move 进 thread::spawn,lock().unwrap() 后修改再 join;Rust 编译器会阻止跨线程共享 &mut T(未实现 Sync)和把非 Send 类型 move 进线程;如需 TSan 检查可用 RUSTFLAGS="-Z sanitizer=thread" cargo +nightly test。
适用边界 · 注意事项
- 程序没有并发或共享状态时无需这些工具。
- 仅需内存泄漏/越界检查时应改用 Memcheck 而非 Helgrind/TSan。
- Rust 中若编译器已保证无数据竞争,通常无需 TSan,除非涉及 unsafe 或 FFI。