欢迎访问个人网络日志🌹🌹知行空间🌹🌹
1.何为RAII
RAII
是Resource Acquisition Is Initialization
的缩写,是由C++之父Bjarne Stroustrup
提出来的,直接翻译过来就是资源获取即初始化,是一个非常强大的编程概念。RAII
理念是借助对象的作用域/生存周期来管理资源,因此也有呼声将其更名为Scope-Bound Resource Management
。在这个概念中资源所指的不仅是内存,也可以指文件描述符,套接字,数据库句柄等。资源的生命周期等同于资源对象的作用域。RAII
是C++
中管理资源、避免内存泄漏的好方法。在C++
中,在创建一个类对象时会自动调用类的构造函数,对象超出作用域时会自动调用析构函数。RAII
的思想就是将资源与对象的生命周期绑定。
RAII
可以总结为两点,
- 将资源封装到类中
- 构造函数获取资源并初始化类中的常量,否则要抛出异常
- 释放资源,不抛异常
- 通过
RAII
型的类的实例来使用资源- 资源的生命周期和类的实例的生命周期相同
带有open/close,lock/unlock,init/copyFrom/destroy
成员函数的类通常不是RAII
型的类。
C++中,STL
中的很多类都遵守RAII
规则,如std::string/std::vector
等,都是在构造函数中获取资源,在析构函数中自动清除,不需要显式的清除资源。STL
中还提供了一些遵守RAII
规则的封装器用以管理用户提供的资源。如:
std::unique_ptr/std::shared_ptr
用于管理动态内存std::lock_guard/std::unique_lock/std::shared_lock
用以管理互斥锁
2.RAII型的类和使用
以unique_lock
为例说明RAII
型类的使用。
#include <memory>
#include <iostream>
#include <thread>
#include <functional>
#include <mutex>
#include <chrono>
using namespace std;
void increase(std::string name, int n)
{
for(auto i=0; i<n; i++) {
std::cout << name << ": " << i << std::endl;
std::this_thread::sleep_for(200ms);
}
}
int main()
{
int n = 5;
auto f1 = std::bind(&increase, "t1", std::placeholders::_1);
auto f2 = std::bind(&increase, "t2", std::placeholders::_1);
auto t1 = std::make_unique<std::thread>(std::bind(f1, n));
auto t2 = std::make_unique<std::thread>(std::bind(f2, n));
t1->join();
t2->join();
}
// t1: 0t2
// : 0
// t2: 1
// t1: 1
// t2: 2
// t1: 2
// t2: 3
// t1: 3
// t2: 4
// t1: 4
上面的代码中,没有做线程同步,两个线程的输出混淆在一起,这不是我们想要的结果。最简单的方式就是使用互斥锁,
void increase(std::string name, int n)
{
mtx.lock();
for(auto i=0; i<n; i++) {
std::cout << name << ": " << i << std::endl;
std::this_thread::sleep_for(200ms);
}
mtx.unlock();
}
// t1: 0
// t1: 1
// t1: 2
// t1: 3
// t1: 4
// t2: 0
// t2: 1
// t2: 2
// t2: 3
// t2: 4
上面互斥锁的使用,需要手动的设置lock
和unlock
,这不符合RAII
的思想,而使用unique_lock/lock_guard
,可以在创建lock
变量的时候就自动上锁,在lock
变量超出作用域,生命周期结束变量销毁时会自动释放锁。其中unique_lock
还可以接受defer_lock
可以推迟加锁,此时需要手动的上锁和释放锁unique_lock
就不符合RAII
规则了。
void increase(std::string name, int n)
{ std::unique_lock<std::mutex> ul(mtx);
for(auto i=0; i<n; i++) {
std::cout << name << ": " << i << std::endl;
std::this_thread::sleep_for(200ms);
}
}
// t1: 0
// t1: 1
// t1: 2
// t1: 3
// t1: 4
// t2: 0
// t2: 1
// t2: 2
// t2: 3
// t2: 4
在3中,作者基于RAII
原则实现了一个互斥锁,其功能和std::lock_guard
类似。
其他的诸如管理文件描述符,套接字,登陆信息的上下文等,使用RAII
规则时类似。