• RAII技术学习


    “Resource Acquisition is Initialization”
    资源获取即初始

    一,什么是RAII

    RAII中的R代表资源,计算机中的一切可由程序员管理的,如指针的申请的内存,开启占用的文件描述符,创建与销毁的锁等等
    对于资源的一般操作即就是

    • 申请
    • 使用
    • 释放

    为了避免一些人为与非人为的因素造成资源的未释放或者是未初始化的野指针的问题,如没有delte的内存,没有unlock的锁,所诞生出的技术

    因素

    std::mutex m;void fn() 
    {
        m.lock();                 
       //read
        使用资源
        //wrire
        m.unlock();                
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果我们忘记释放锁,或者wirte的时候出现err,或者因为中间庞大的逻辑与架构导致对解锁的管理难以掌控,那么别的线程再也无法加锁成功,而且还会导致一直阻塞

    特殊情况

    
    int main()
    {
    	try
    	{
    		funcOne();
    	}
    	catch (const std::exception& e)
    	{
    		cerr << "Exception caught!" << endl;
    	}
        return 0;
    }
     
    void funcOne()
    {
    	string str1;
    	string* str2 = new string();
    	funcTwo();
    	delete str2;
    }
     
    void funcTwo()
    {
    	ofstrean ofs;
    	ofs.open("filename");
    	throw exception();
    	ofs.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    当functwo的函数抛出异常时,根据栈展开,最近的处理程序在main,那么程序就会跳回main,ofs.close()将没有执行机会,资源就会来不及释放,当然也可以每跳一层释放一层但是会显得逻辑臃肿

    为了解决这样的问题

    使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入;

    RAII最为智慧的一点是将资源的释放方式由程序员控制,将控制时机交给操作系统

    二,经典实现

    template <typename T>
    class lock_guard
    {
    private:
        T _mutex;
    public:
        explicit lock_guard(T &mutex) : _mutex(mutex)
        {
            _mutex.lock();
        }
        ~lock_guard()
        {
            _mutex.unlock();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    像下面这种情况 这把锁就会被初始化时加上,在栈对象被销毁时解锁

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    智能指针

    对于内存的管理一直是让c/c++程序员头疼的事情,内存泄漏(即 malloc() 内存在对应的 free() 调用执行后永不被释放),对于未初始化的野指针的错误使用,或者错误指向造成的问题,但是我们可以把内存的释放时机交给操作系统来决定

    // 使用RAII思想设计的SmartPtr类
    template<class T>
    class SmartPtr {
        public:
            SmartPtr(T* ptr = nullptr)
            : _ptr(ptr)
            {}
            ~SmartPtr()
            {
            if(_ptr)
            delete _ptr;
        }
        private:
            T* _ptr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    RAII的特点一般即为,利用构造函数申请资源,利用析构函数释放资源,所以可以不止于此,对于一些其他的资源如文件描述符,都可以利用类对象的特性实现管理

    一些特殊实现

    5 struct Timer {
      6   std::chrono::time_point<std::chrono::system_clock> start, end;
      7   std::chrono::duration<float> duration;
      8 
      9   Timer() {
     10     start = std::chrono::high_resolution_clock::now();
     11   }
     12 
     13   ~Timer() {
     14     end = std::chrono::high_resolution_clock::now();
     15     duration = end - start;
     16 
     17     float ms = duration.count() * 1000.0f;
     18     std::cout << "Timer took " << ms << "ms " << std::endl;
     19   }
     20 };                                                                                                                                        
     21 
     22 void Function() {
     23   Timer timer;// 为整个作用域计时
     24 
     25   for(int i = 0; i < 100; i++){
     26     std::cout << "hello\n";
     27   }
     28 }
     29 
     30 int main(int argc, char **argv) {
     31   Function();
     32 
     33   std::cin.get();
     34 }
    ~                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    注:

    要做到资源的最精细化管理,还是要自己学会自己动手,因为RaII也无法做到最精细的管理,对比其他类似GO语言等的Gc机制,完全把回收的工作交给操作系统巡回使得解放程序员,但会造成调用时的性能波动开销,也会导致一定的暂停响应状态( 感兴趣的可以下去查一查),因此最精确的内存控制还得靠你的细心思考与设计;

  • 相关阅读:
    手把手教你用C语言制作七夕流星雨---优雅永不过时(详细教程)
    Shellcode——绕过31
    SpringMVC转发和重定向
    【LeetCode】1687. 从仓库到码头运输箱子
    文字转语音怎么做?分享三种配音方法,真人语音很逼真
    c++的priority_queue各种使用方法
    记录 | 修改docker存储路径
    网络安全——使用Linux系统命令对后门端口进行查杀
    【老生谈算法】matlab实现实验遗传算法与优化设计——遗传算法
    如何设置和取消ZIP文件的自动加密
  • 原文地址:https://blog.csdn.net/m0_63515013/article/details/125850295