目录
介绍
RAII :Resource Acquisition Is Initialisation (RAII)
(任何资源的获取都应该发生在类的构造函数中,资源的释放应发生在析构函数中,即资源的生命周期与对象绑定)
RAII是一种使用在面向对象语言中的资源(内存,互斥锁,或者文件描述符)管理机制,使用RAII的语言中,最出名的当属C++和RUST。对C++来说,许多公司已经开始禁用裸指针(强制使用基于RAII的智能指针)来避免内存泄漏。而RUST,正是因为强制RAII机制使得其拥有了绝对的内存安全。
RAII技术在锁上的应用
互斥锁
std::mutex mut;
int write_to_a_file_descriptor(std::string content)
{
mut.lock();
// critical area below (might throw exception)
// Writing content to a file descriptor...
// Critical areas above
mut.unlock();
}
以上代码展示了一个将字符串写进某个文件描述符的函数,并且这个函数会被很多线程并行调用 (这种情况在高并发线上服务的logger中非常常见),因此这个共用的文件描述符必须用一个互斥锁保护起来,否则不同线程的字符串会混在一起。这段代码看起来仿佛没有问题,但是如果当写IO时是抛出了异常,call stack会被直接释放,也就意味着 unlock
方法不会执行,造成永久的死锁。这个问题可以像java一样用一个try-catch语句来避免但是也会让代码变得臃肿和难看。并且在复杂的逻辑中,往往很可能会忘了解锁,或者花很多精力来管理锁的获得和释放(如果在一个函数调用中有多处返回,每个return statement之前都需要 unlock
)。
这就是RAII发挥其威力的时候了,下面一段代码将展示如何用 lock_guard
来使我们的代码异常安全并且整洁。
std::mutex mut;
int write_to_a_file_descriptor(std::string content)
{
std::lock_guard<std::mutex> lock(mut);
// critical area below (might throw exception)
// Writing content to a file descriptor...
// Critical areas above
}
lock_guard
保证在函数返回之后释放互斥锁,因此使得开发人员不需要为抛出异常的情况担心且不需手动释放锁。但是 lock_guard
是如何做到的呢?笔者将尝试自己手动实现一个 lock_guard
template <typename T>
class lock_guard
{
private:
T _mutex;
public:
explicit lock_guard(T &mutex) : _mutex(mutex)
{
_mutex.lock();
}
~lock_guard()
{
_mutex.unlock();
}
};
从以上