C++编程指南17 - 使用 RAII(资源获取即初始化),避免直接调用 lock()/unlock()
一:概述
本指南的核心思想是避免手动管理锁的获取和释放,而是利用 C++ 的 RAII(Resource Acquisition Is Initialization) 机制,通过 std::unique_lock
或 std::lock_guard
来自动管理锁的生命周期。
如果手动调用 lock()
和 unlock()
,很容易导致资源泄漏或死锁,因为:
- 代码可能在
unlock()
之前 提前返回,导致锁未释放。 - 代码可能在
unlock()
之前 抛出异常,导致锁未释放。 - 维护代码的人可能会忘记调用
unlock()
,导致锁未释放。
二:错误示例
#include <mutex>
std::mutex mtx;
void do_stuff()
{
mtx.lock(); // 手动加锁
// ... do stuff ...
mtx.unlock(); // 手动解锁(容易出错)
}
出错场景1:
void do_stuff()
{
mtx.lock();
if (some_condition)
return; // 忘记 unlock(),锁永远不会释放
mtx.unlock();
}
出错场景2:
void do_stuff()
{
mtx.lock();
if (some_condition)
throw std::runtime_error("Error occurred"); // 忘记 unlock(),锁未释放
mtx.unlock();
}
三:解决方法
C++ 提供了 std::lock_guard
和 std::unique_lock
这两个 RAII 机制的锁管理类,确保锁在作用域结束时自动释放,无需手动调用 unlock()
。
#include <mutex>
std::mutex mtx;
void do_stuff()
{
std::lock_guard<std::mutex> lck(mtx); // 构造时自动加锁,析构时自动解锁
// ... do stuff ...
} // 离开作用域时,自动调用 unlock()
#include <mutex>
std::mutex mtx;
void do_stuff()
{
std::unique_lock<std::mutex> lck(mtx); // 自动加锁
// ... do stuff ...
lck.unlock(); // 如果需要手动释放,可以显式调用 unlock()
}