【RAII】RAII 技术(内存安全解决技术/自动化解锁技术)

RAII(Resource Acquisition Is Initialization)是一种资源管理机制,确保资源在对象生命周期内正确管理,尤其在C++和Rust中。本文介绍了RAII在互斥锁和智能指针上的应用,通过示例展示了如何利用RAII实现异常安全和简洁的代码,同时提到了RAII的局限性和潜在的缺点。
摘要由CSDN通过智能技术生成

目录

介绍

RAII技术在锁上的应用

互斥锁

RAII技术在指针上的应用

智能指针


介绍

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();
    }
};

从以上

C++中,unique_lock是一个用于管理互斥锁的RAII(资源获取即初始化)类。它提供了一种更灵活的方式来管理互斥锁的加锁和解锁操作。 unique_lock的解锁操作非常简单,只需要调用其成员函数unlock()即可。例如: ```cpp #include <iostream> #include <mutex> std::mutex mtx; void foo() { std::unique_lock<std::mutex> lock(mtx); // 互斥锁已经在构造unique_lock对象时被加锁 // 执行一些需要保护的操作 // 解锁互斥锁 lock.unlock(); // 在解锁后可以执行一些不需要互斥保护的操作 // 再次加锁互斥锁 lock.lock(); // 执行一些需要保护的操作 // 解锁互斥锁 lock.unlock(); } int main() { foo(); return 0; } ``` 在上面的示例中,我们首先创建了一个std::mutex对象mtx,然后在函数foo()中创建了一个unique_lock对象lock,并将mtx作为参数传递给它。在unique_lock对象的构造函数中,互斥锁会被自动加锁。然后我们可以执行一些需要保护的操作。当我们调用lock.unlock()时,互斥锁会被解锁,这样我们就可以执行一些不需要互斥保护的操作。最后,我们可以再次调用lock.lock()来重新加锁互斥锁,并执行一些需要保护的操作。最后,当unique_lock对象超出作用域时,析构函数会自动解锁互斥锁。 需要注意的是,unique_lock对象的unlock()和lock()成员函数可以在任何时候调用,而不仅仅是在构造函数和析构函数中。这使得我们可以更灵活地控制互斥锁的加锁和解锁操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值