std::scoped_lock 原理
核心机制:RAII(Resource Acquisition Is Initialization)
cpp
std::scoped_lock lk(m1, m2); // 构造时获取锁
// ... 临界区代码
// lk 析构时自动释放锁
工作流程
cpp
// std::scoped_lock 的简化实现
template<typename... Mutexes>
class scoped_lock {
std::tuple<Mutexes&...> m_mutexes;
public:
scoped_lock(Mutexes&... mutexes) : m_mutexes(mutexes...) {
std::lock(mutexes...); // 构造时用 std::lock 获取所有锁
}
~scoped_lock() {
std::apply([](auto&... m) { (m.unlock(), ...); }, m_mutexes);
// 析构时释放所有锁
}
// 禁止拷贝和移动
scoped_lock(const scoped_lock&) = delete;
scoped_lock& operator=(const scoped_lock&) = delete;
};
为什么更安全?
| 特性 | std::lock |
std::scoped_lock |
|---|---|---|
| 异常安全 | ❌ 需要手动 unlock |
✅ 自动析构释放 |
| 忘记释放 | ❌ 可能死锁 | ✅ 不可能忘记 |
| 早期返回 | ❌ 需要手动处理 | ✅ 自动释放 |
| 代码简洁 | ❌ 需要 try-catch | ✅ 一行搞定 |
对比示例
cpp
// 不安全的方式
void unsafe() {
std::lock(m1, m2);
if (some_condition) {
return; // 忘记释放锁!
}
counter++;
m1.unlock(); // 如果抛异常,这里不会执行
m2.unlock();
}
// 安全的方式
void safe() {
std::scoped_lock lk(m1, m2); // RAII
if (some_condition) {
return; // lk 自动析构,释放锁
}
counter++;
// lk 自动析构,释放锁
}
异常安全性
cpp
void exception_safe() {
std::scoped_lock lk(m1, m2);
throw std::runtime_error("error"); // 抛出异常
// lk 析构函数会被调用,自动释放锁
}
总结
std::scoped_lock 更安全是因为:
- RAII:构造时获取,析构时释放
- 异常安全:即使抛异常也会释放
- 无脑使用:不需要考虑忘记释放
- 代码简洁:一行搞定,减少出错可能
本质 :std::scoped_lock = std::lock + RAII 包装器
std::scoped_lock 出现版本
std::scoped_lock 是 C++17 引入的。
C++ 版本中的锁相关特性
| C++版本 | 新增特性 | 说明 |
|---|---|---|
| C++11 | std::mutex, std::lock_guard, std::unique_lock, std::lock |
基础线程同步 |
| C++14 | std::shared_timed_mutex |
共享锁 |
| C++17 | std::scoped_lock |
多锁 RAII 包装器 |
| C++20 | std::jthread, std::atomic_ref |
更多线程工具 |
C++11-C++14 的替代方案
在 C++17 之前,人们常用:
cpp
// C++11/14 方式
std::unique_lock<std::mutex> lk1(m1, std::defer_lock);
std::unique_lock<std::mutex> lk2(m2, std::defer_lock);
std::lock(lk1, lk2); // 死锁安全,但代码冗长
C++17 的简洁写法
cpp
// C++17+ 方式
std::scoped_lock lk(m1, m2); // 一行搞定
编译器支持
| 编译器 | 最低版本支持 |
|---|---|
| GCC | 7.0+ |
| Clang | 5.0+ |
| MSVC | 2017+ |
检查 C++ 版本
cpp
#if __cplusplus >= 201703L
std::scoped_lock lk(m1, m2); // C++17+
#else
// C++11/14 兼容写法
std::unique_lock<std::mutex> lk1(m1, std::defer_lock);
std::unique_lock<std::mutex> lk2(m2, std::defer_lock);
std::lock(lk1, lk2);
#endif
总结 :std::scoped_lock 是 C++17 的改进,让多锁管理变得更简单安全。