Mutex::AutoLock介绍

来源:互联网 发布:睿威仕监控软件下载 编辑:程序博客网 时间:2024/06/14 09:22

有段时间没有写c++代码了,最近研究framework代码,发现很多地方用到了Mutex::AutoLock互斥锁,JAVA代码常用synchronized同步方法或者同步块。

synchronized属于代码段的方法同步。

  有几点需要注意:

  1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

  2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

  3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型),也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。


       言归正传Mutex

       互斥类—Mutex
Mutex是互斥类,用于多线程访问同一个资源的时候,保证一次只有一个线程能访问该资源。在《Windows核心编程》①一书中,对于这种互斥访问有一个很形象的比喻:想象你在飞机上如厕,这时卫生间的信息牌上显示“有人”,你必须等里面的人出来后才可进去。这就是互斥的含义。

   

        关于Mutex的使用,除了初始化外,最重要的是lock和unlock函数的使用,它们的用法如下:
 要想独占卫生间,必须先调用Mutex的lock函数。这样,这个区域就被锁住了。如果这块区域之前已被别人锁住,lock函数则会等待,直到可以进入这块区域为止。系统保证一次只有一个线程能lock成功。
 当你“方便”完毕,记得调用Mutex的unlock以释放互斥区域。这样,其他人的lock才可以成功返回。
 另外,Mutex还提供了一个trylock函数,该函数只是尝试去锁住该区域,使用者需要根据trylock的返回值来判断是否成功锁住了该区域。
注意 以上这些内容都和Raw API有关,不了解它的读者可自行学习相关知识。在Android系统中,多线程也是常见和重要的编程手段,务必请大家重视。Mutex类确实比Raw API方便好用,不过还是稍显麻烦。

(2)AutoLock类是定义在Mutex内部的一个类,它其实是一帮“懒人”搞出来的,为什么这么说呢?先来看看使用Mutex有多麻烦:
 显示调用Mutex的lock。
 在某个时候记住要调用该Mutex的unlock。
以上这些操作都必须一一对应,否则会出现“死锁”!在有些代码中,如果判断分支特别多,你会发现unlock这句代码被写得比比皆是,如果稍有不慎,在某处就会忘了写它。有什么好办法能解决这个问题吗?终于有人想出来一个好办法,就是充分利用了C++的构造和析构函数,只需看一看AutoLock的定义就会明白。代码如下所示:

  1. class Autolock {  
  2.     public:  
  3.         //构造的时候调用lock。  
  4.         inline Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }  
  5.         inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }  
  6.         //析构的时候调用unlock。  
  7.         inline ~Autolock() { mLock.unlock(); }  
  8.     private:  
  9.         Mutex& mLock;  
  10.     }; 

AutoLock的用法很简单:
 先定义一个Mutex,如 Mutex xlock。
 在使用xlock的地方,定义一个AutoLock,如 Mutex::Autolock autoLock(xlock)。
由于C++对象的构造和析构函数都是自动被调用的,所以在AutoLock的生命周期内,xlock的lock和unlock也就自动被调用了,这样就省去了重复书写unlock的麻烦,而且lock和unlock的调用肯定是一一对应的,这样就绝对不会出错。

很巧妙的利用的C++ 作用域内的对象的生命周期的原理,来自动调用析构函数,同时释放掉相关lock。