Slim Reader/Writer锁死锁问题及多独占线程性能异常排查
问题分析与解决
核心问题1:共享锁的误用
线程t1使用**共享锁(Shared)**保护写操作,完全违背了SRW锁的设计原则:
- 共享锁仅用于读操作,允许多个线程同时持有,保证读操作的并发安全;
- 写操作必须使用独占锁(Exclusive),确保同一时间只有一个线程修改数据。
在共享锁下修改myglobalvar会直接导致数据竞争,同时引发锁等待的连锁问题:当t1长时间持有共享锁(Sleep(500))时,所有请求独占锁的线程(t2和主线程)都会被阻塞,无法执行,这就是程序变慢的直接原因。
核心问题2:SRW锁未显式初始化
虽然全局变量会被系统零初始化,且Windows的SRWLock零值是合法初始状态,但显式初始化是良好的编程习惯,可避免因环境差异导致的潜在问题。需要在main函数开头添加初始化代码。
核心问题3:锁饥饿与假死隐患
Windows的SRWLock具备写优先特性:当有线程等待独占锁时,后续的共享锁请求会被阻塞,直到独占锁被获取并释放。但你的代码中t1持有共享锁的时间长达500ms,远超过独占锁线程的执行时间,导致独占锁线程大部分时间都在等待,出现严重的写饥饿,表现为程序运行异常缓慢。极端情况下,若系统调度导致t1释放共享锁后立刻重新获取,独占锁线程可能长时间无法抢到锁,看似“死锁”(实际是饥饿导致的假死)。
修复后的代码
#include <iostream> #include <Windows.h> SRWLOCK mainLock; char myglobalvar; int main() { InitializeSRWLock(&mainLock); // 显式初始化SRW锁 myglobalvar = '0'; HANDLE t1 = CreateThread(nullptr, 0, [](void*)->DWORD { while (true) { // 写操作必须用独占锁 AcquireSRWLockExclusive(&mainLock); myglobalvar = 'S'; Sleep(500); ReleaseSRWLockExclusive(&mainLock); } return 0; }, nullptr, 0, nullptr); Sleep(50); HANDLE t2 = CreateThread(nullptr, 0, [](void*)->DWORD { while (true) { AcquireSRWLockExclusive(&mainLock); myglobalvar = 'E'; ReleaseSRWLockExclusive(&mainLock); Sleep(1); // 添加短暂休眠,避免过度抢占CPU } return 0; }, nullptr, 0, nullptr); for (int i = 0; i < 100; i++) { AcquireSRWLockShared(&mainLock); // 读操作使用共享锁 std::cout << myglobalvar << '\n'; ReleaseSRWLockShared(&mainLock); Sleep(50); } // 清理线程句柄,避免资源泄漏 CloseHandle(t1); CloseHandle(t2); return 0; }
额外优化建议
- 严格读写分离:读操作使用共享锁,写操作使用独占锁,最大化并发效率;
- 缩短锁持有时间:避免在锁内执行
Sleep等耗时操作,将非临界逻辑移到锁外; - 增加退出条件:实际程序中需为无限循环线程添加终止逻辑,防止线程一直运行;
- 资源清理:使用
CloseHandle关闭线程句柄,避免系统资源泄漏。
内容的提问来源于stack exchange,提问作者Badasahog
相关产品推荐
相关产品推荐

