菜鸟笔记
提升您的技术认知

c raii机制和原理-ag真人官方网

1.什么是raii
raii(resource acquisition is initialization)是由c 之父bjarne stroustrup提出的,中文翻译为资源获取即初始化,他说:使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入;

2.raii的原理
资源的使用一般经历三个步骤a.获取资源 b.使用资源 c.销毁资源,但是资源的销毁往往是程序员经常忘记的一个环节,所以程序界就想如何在程序员中让资源自动销毁呢?c 之父给出了解决问题的方案:raii,它充分的利用了c 语言局部对象自动销毁的特性来控制资源的生命周期。给一个简单的例子来看下局部对象的自动销毁的特性:

#include 
using namespace std;
class person {
  public:
      person(const std::string name = "", int age = 0) : 
      name_(name), age_(age) {
            std::cout << "init a person!" << std::endl;
      }
      ~person() {
            std::cout << "destory a person!" << std::endl;
      }
      const std::string& getname() const {
            return this->name_;
      }    
      int getage() const {
            return this->age_;
      }      
  private:
      const std::string name_;
      int age_;  
};
int main() {
    person p;
    return 0;
}
编译并运行:
g   person.cpp -o person
./person 
运行结果:
init a person!
destory a person!

从person class可以看出,当我们在main函数中声明一个局部对象的时候,会自动调用构造函数进行对象的初始化,当整个main函数执行完成后,自动调用析构函数来销毁对象,整个过程无需人工介入,由操作系统自动完成;于是,很自然联想到,当我们在使用资源的时候,在构造函数中进行初始化,在析构函数中进行销毁。整个raii过程我总结四个步骤:

a.设计一个类封装资源

b.在构造函数中初始化

c.在析构函数中执行销毁操作

d.使用时声明一个该对象的类

3.raii的应用
本节主要通过一个简单的例子来说明如何将raii应用到我们的代码中。linux下经常会使用多线程技术,说到多线程,就得提到互斥锁,互斥锁主要用于互斥,互斥是一种竞争关系,用来保护临界资源一次只被一个线程访问,按照我们前面的分析,我们封装一下posix标准的互斥锁:

#include 
#include 
#include 
class mutex {
 public:
  mutex();
  ~mutex();
  void lock();
  void unlock(); 
 private:
  pthread_mutex_t mu_;
  // no copying
  mutex(const mutex&);
  void operator=(const mutex&);
};
#include "mutex.h"
static void pthreadcall(const char* label, int result) {
  if (result != 0) {
    fprintf(stderr, "pthread %s: %s\n", label, strerror(result));
  }
}
mutex::mutex() { pthreadcall("init mutex", pthread_mutex_init(&mu_, null)); }
mutex::~mutex() { pthreadcall("destroy mutex", pthread_mutex_destroy(&mu_)); }
void mutex::lock() { pthreadcall("lock", pthread_mutex_lock(&mu_)); }
void mutex::unlock() { pthreadcall("unlock", pthread_mutex_unlock(&mu_)); }

写到这里其实就可以使用mutex来锁定临界区,但我们发现mutex只是用来对锁的初始化和销毁,我们还得在代码中调用lock和unlock函数,这又是一个对立操作,所以我们可以继续使用raii进行封装,代码如下:

#include "mutex.h"
class  mutexlock {
 public:
  explicit mutexlock(mutex *mu)
      : mu_(mu)  {
    this->mu_->lock();
  }
  ~mutexlock() { this->mu_->unlock(); }
 private:
  mutex *const mu_;
  // no copying allowed
  mutexlock(const mutexlock&);
  void operator=(const mutexlock&);
};

到这里我们就真正封装了互斥锁,下面我们来通过一个简单的例子来使用它,代码如下:

#include "mutexlock.hpp"
#include 
#include 
#define    num_threads     10000
int num=0;
mutex mutex;
void *count(void *args) {
    mutexlock lock(&mutex);
    num  ;
}
int main() {
    int t;
    pthread_t thread[num_threads];
    for( t = 0; t < num_threads; t  ) {   
        int ret = pthread_create(&thread[t], null, count, null);
        if(ret) {   
            return -1;
        }   
    }
    for( t = 0; t < num_threads; t  )
        pthread_join(thread[t], null);
    std::cout << num << std::endl;
    return 0;
}

编译并运行:g test_mutexlock.cpp mutexlock.hpp mutex.cpp mutex.h -o test_mutexlock -lpthread
./test_mutexlock
运行结果:10000 符合预期(可以去掉mutexlock lock(&mutex);试试看看结果如何?)

网站地图