1. 自动锁的概念与RAII设计思想
在C++多线程编程中,手动管理互斥锁(mutex)的加锁和解锁操作极易出错。我曾在一个电商系统的订单处理模块中,就遇到过因为漏写unlock()导致的死锁问题——高峰时段整个系统卡死,最终通过gdb回溯调用栈才定位到问题。这正是我们需要自动锁的根本原因。
RAII(Resource Acquisition Is Initialization)是C++特有的资源管理范式,其核心思想是:
- 对象构造时获取资源(如锁)
- 对象生命周期内保持资源有效
- 对象析构时自动释放资源
这种机制完美契合互斥锁的使用场景。想象一下保险箱的使用过程:当你拿到保险箱时自动上锁(构造),使用期间保持锁定(生命周期),归还时自动解锁(析构)。这正是我们要实现的自动锁行为模式。
2. 自动锁的完整实现方案
2.1 基础互斥锁封装
先实现一个基础的Mutex类,它封装了原生pthread_mutex_t:
cpp复制class Mutex {
public:
Mutex() {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&mutex_, &attr);
}
~Mutex() {
pthread_mutex_destroy(&mutex_);
}
void lock() {
int ret = pthread_mutex_lock(&mutex_);
if (ret != 0) {
throw std::runtime_error("lock failed");
}
}
void unlock() {
int ret = pthread_mutex_unlock(&mutex_);
if (ret != 0) {
throw std::runtime_error("unlock failed");
}
}
private:
pthread_mutex_t mutex_;
};
关键设计点:
- 使用PTHREAD_MUTEX_ERRORCHECK类型便于调试
- 每个操作都检查返回值并抛出异常
- 禁用拷贝构造和赋值(通过=delete)
2.2 自动锁守卫实现
核心的自动锁通过守卫类实现:
cpp复制class LockGuard {
public:
explicit LockGuard(Mutex& mutex)
: mutex_(mutex), locked_(true) {
mutex_.lock();
}
~LockGuard() {
if (locked_) {
mutex_.unlock();
}
}
// 禁止拷贝
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
// 允许移动
LockGuard(LockGuard&& other)
: mutex_(other.mutex_), locked_(other.locked_) {
other.locked_ = false;
}
private:
Mutex& mutex_;
bool locked_;
};
这个实现有几个精妙之处:
- 移动语义支持:允许锁守卫在函数间传递
- locked_标志:确保不会重复解锁
- 严格的异常安全保证
2.3 条件变量集成
实际项目中经常需要条件变量配合使用:
cpp复制class Condition {
public:
explicit Condition(Mutex& mutex)
: mutex_(mutex) {
pthread_cond_
