C++11在Windows下共享访问极罕见时,如何最优锁定资源?
最优实现方案:Windows SRWLOCK 轻量级读写锁
针对你这种读操作(规则检查)占99.9%、写操作(规则增删)极少,且要求“增删规则时禁止检查操作执行”的场景,Windows原生的SRWLOCK(轻量级读写锁)是最匹配的选择,既能保证读操作的极致性能,又能在写操作期间完全阻塞读请求。
核心逻辑
- 规则检查(读操作):使用共享锁,多个读线程可同时进入,无锁竞争时几乎没有性能开销;
- 规则增删(写操作):使用独占锁,一旦获取锁,所有读操作会被阻塞,直到写操作完成并释放锁,彻底保证“增删时不执行检查”的要求;
SRWLOCK是用户态锁,仅在出现锁竞争时才会进入内核态,而你的写操作极少,几乎不会触发内核态切换,性能远超普通互斥锁。
代码实现示例
#include <windows.h> #include <vector> #include <algorithm> // 假设你的规则类型定义如下 struct Rule { DWORD targetPid; bool allow; bool Match(DWORD pid) const { return targetPid == pid; } bool operator==(const Rule& other) const { return targetPid == other.targetPid && allow == other.allow; } }; class RuleManager { private: SRWLOCK m_srwLock = SRWLOCK_INIT; std::vector<Rule> m_rules; public: // 规则检查:读操作,获取共享锁 bool CheckPermission(DWORD pid) { AcquireSRWLockShared(&m_srwLock); bool result = false; for (const auto& rule : m_rules) { if (rule.Match(pid)) { result = rule.allow; break; } } ReleaseSRWLockShared(&m_srwLock); return result; } // 添加规则:写操作,获取独占锁 void AddRule(const Rule& newRule) { AcquireSRWLockExclusive(&m_srwLock); m_rules.push_back(newRule); ReleaseSRWLockExclusive(&m_srwLock); } // 删除规则:写操作,获取独占锁 void RemoveRule(const Rule& ruleToRemove) { AcquireSRWLockExclusive(&m_srwLock); auto it = std::find(m_rules.begin(), m_rules.end(), ruleToRemove); if (it != m_rules.end()) { m_rules.erase(it); } ReleaseSRWLockExclusive(&m_srwLock); } };
为什么这个方案比你考虑的选项更好?
- 比
std::mutex性能高:std::mutex每次检查都需要加解锁,即使没有竞争也有开销;而SRWLOCK的共享锁在无写操作时是原子操作,几乎零开销; - 比Critical Sections更优:Windows 8.1+上Critical Sections的性能确实不如
std::mutex,而SRWLOCK作为专门的读写锁,读密集场景下的性能远高于两者; - 完全满足业务要求:写操作时独占锁会阻塞所有读请求,确保增删规则期间没有检查操作执行,读操作时无额外负担,完美平衡安全与性能。
备选方案:双缓冲无锁发布(适合允许读旧数据的场景)
如果你的需求可以放宽为“增删规则时允许检查操作读取旧规则列表”,而非严格禁止检查执行,那么双缓冲无锁方案能实现极致的读性能:
- 维护两个规则列表:一个活跃列表供读操作直接访问,一个备用列表供写操作修改;
- 写操作时复制活跃列表到备用列表,修改后通过原子指针替换活跃列表;
- 读操作无需任何锁,直接读取原子指针指向的列表。
简化代码示例:
#include <atomic> #include <vector> #include <memory> struct Rule { DWORD targetPid; bool allow; bool Match(DWORD pid) const { return targetPid == pid; } }; class RuleManager { private: std::atomic<std::shared_ptr<std::vector<Rule>>> m_activeRules; public: RuleManager() : m_activeRules(std::make_shared<std::vector<Rule>>()) {} bool CheckPermission(DWORD pid) { auto rules = m_activeRules.load(); // 原子加载,无任何锁开销 bool result = false; for (const auto& rule : *rules) { if (rule.Match(pid)) { result = rule.allow; break; } } return result; } void AddRule(const Rule& newRule) { auto oldRules = m_activeRules.load(); auto newRules = std::make_shared<std::vector<Rule>>(*oldRules); newRules->push_back(newRule); m_activeRules.store(newRules); // 原子替换,旧列表自动回收 } };
这个方案的读操作完全无锁,但写操作需要复制整个规则列表,适合规则数量不多的场景。
内容的提问来源于stack exchange,提问作者OneAndOnly
相关产品推荐
相关产品推荐

