Android中同步类Mutex(AutoMutex)与Condition。

来源:互联网 发布:php手机网站源码下载 编辑:程序博客网 时间:2024/04/30 02:03



在Android中,封装的同步类主要有MutexAutoMutex)与Condition

这两个类在android中被大量的使用,这也说明这两个类是非常重要的。

下面我们就从3个方面来分析他们。

  1. 它们是什么,他们的实现原理,即what

  2. 为什么要这么去实现它,即why

  3. 我们怎么去用他们,即how

    这样我们就对它们有个深入的理解,不但知其然,而且知其所以然。

     

    MutexAutoMutex)与Condition代码分析

     

    1. MutexAutoMutex)代码分析

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

          它的代码实现如下

      system/core/include/utils/Mutex.h

      /*

       * Copyright (C) 2007 The Android Open SourceProject

       *

       * Licensed under the Apache License, Version2.0 (the "License");

       * you may not use this file except incompliance with the License.

       * You may obtain a copy of the License at

       *

       *     http://www.apache.org/licenses/LICENSE-2.0

       *

       * Unless required by applicable law or agreedto in writing, software

       * distributed under the License is distributedon an "AS IS" BASIS,

       * WITHOUT WARRANTIES OR CONDITIONS OF ANYKIND, either express or implied.

       * See the License for the specific languagegoverning permissions and

       * limitations under the License.

       */

       

      #ifndef_LIBS_UTILS_MUTEX_H

      #define_LIBS_UTILS_MUTEX_H

      //ifndef/define/endif主要目的是防止头文件的重复包含和编译

      //如果有两个地方都包含这个头文件,就不会出现两次包含的情况,良好的编程习惯。

       

      #include<stdint.h>//引用标准库的头文件

      #include<sys/types.h>

      #include<time.h>

       

      #ifdefined(HAVE_PTHREADS)

      # include<pthread.h>

      #endif

       

      #include<utils/Errors.h>

       

      //---------------------------------------------------------------------------

      namespaceandroid { //android的命名空间,使用的时候用“using namespace android;

                         //C++里的“using namespace std;”一样。

      //---------------------------------------------------------------------------

       

      class Condition;

       

      /*

       * Simple mutex class. The implementation is system-dependent.

       *

       * The mutex must be unlocked by the threadthat locked it. They are not

       * recursive, i.e. the same thread can't lockit multiple times.

       */

      class Mutex {

      public:

         enum {

             PRIVATE = 0,

             SHARED = 1

         };

      //两种类型:PRIVATE是进程内部使用的;SHARED是适用于跨进程共享的。

         //如不指定,缺省是PRIVATE的类型。

         

                     Mutex();   //构造函数

                     Mutex(const char* name);  //构造函数

                     Mutex(int type, const char*name = NULL);  //构造函数,type就是上面的那两种类型

                     ~Mutex();  //析构

       

         // lock or unlock the mutex

         status_t   lock(); //获取锁。如果获取就返回,否则挂起等待

         void       unlock();  //释放锁

       

         // lock if possible; returns 0 on success,error otherwise

         status_t   tryLock();

         //如果当前锁可被获取(未被别的线程获取)就lock,否则就直接返回。

      //返回值:0代表成功;其它值失败。

      //lock()的区别在于不论成功与否都会及时返回,而不是挂起等待。

         // Manages the mutex automatically. It'llbe locked when Autolock is

         // constructed and released when Autolockgoes out of scope.

      //Autolock是为了简化Mutex的使用而定义的,并且充分利用了c++的构造与析构机制

      //可以看出,在构造函数中 mLock.lock()加锁,在析构函数中 mLock.unlock() 解锁。

         //所以,对一个需要加锁的函数来说,我们只需要在函数开始处,声明这样 (Mutex::Autolock autolock(mLock);),一个变量,它就会加锁,

         //等函数退出时,这样一个临时变量就会析构,就会解锁。

      ////android系统里几乎到处都是这种使用,或者AutoMutex _l(mLock)这种使用

      //这两种使用是一样的效果的,因为下面有这样一行代码typedefMutex::Autolock AutoMutex;

      //这种设计师非常优秀的,如果你手动去lockunlock,就有可能

      //忘了unlock,这样的会很容易死锁,死锁在android系统里后果是非常严重,大多数情况都会系统重启

      //大家都知道C++的构造函数析构函数是成对出现的,用了构造函数中 mLock.lock()加锁,

      //在析构函数中 mLock.unlock()解锁这种设计之后,就不会出现忘了unlock的情况了

         class Autolock {

         public:

             inline Autolock(Mutex& mutex) :mLock(mutex) { mLock.lock(); } 

         //其实这里不加inline也是没有关系的,在C++里编译器会自动去检查这个函数体

         //如果函数体逻辑足够简单,会自动把他当成inline函数,为了养成良好的代码习惯,还是要加上                                                               

             inline Autolock(Mutex* mutex) :mLock(*mutex) { mLock.lock(); }

             inline ~Autolock() { mLock.unlock(); }

         private:

             Mutex& mLock;

         };

       

      private:

         friend class Condition; //友元类Condition

         

         // A mutex cannot be copied

                     Mutex(const Mutex&);

         Mutex&      operator = (const Mutex&);

         

      #ifdefined(HAVE_PTHREADS)

         pthread_mutex_t mMutex;

      #else

         void   _init();

         void*  mState;

      #endif

      };

       

      //---------------------------------------------------------------------------

       

      #if defined(HAVE_PTHREADS)

       

      inlineMutex::Mutex() {

         pthread_mutex_init(&mMutex, NULL);

      }

      inlineMutex::Mutex(__attribute__((unused)) const char* name) {

         pthread_mutex_init(&mMutex, NULL);

      }

      inlineMutex::Mutex(int type, __attribute__((unused)) const char* name) {

         if (type == SHARED) {

             pthread_mutexattr_t attr;

             pthread_mutexattr_init(&attr);

             pthread_mutexattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);

             pthread_mutex_init(&mMutex,&attr);

             pthread_mutexattr_destroy(&attr);

         } else {

             pthread_mutex_init(&mMutex, NULL);

         }

      }

      inlineMutex::~Mutex() {

         pthread_mutex_destroy(&mMutex);

      }

      inlinestatus_t Mutex::lock() {

         return -pthread_mutex_lock(&mMutex);

      }

      inline voidMutex::unlock() {

         pthread_mutex_unlock(&mMutex);

      }

      inlinestatus_t Mutex::tryLock() {

         return -pthread_mutex_trylock(&mMutex);

      }

       

      #endif //HAVE_PTHREADS

       

      //---------------------------------------------------------------------------

       

      /*

       * Automatic mutex. Declare one of these at the top of afunction.

       * When the function returns, it will go out ofscope, and release the

       * mutex.

       */

       

      typedefMutex::Autolock AutoMutex;

       

      //---------------------------------------------------------------------------

      }; //namespace android

      // ---------------------------------------------------------------------------

       

      #endif //_LIBS_UTILS_MUTEX_H

       

       1.2 Condition代码分析

      Condition条件类,在多线程同步中,主要是下面这种使用场景使用到condition

      线程A做初始化工作,而其他线程,比如线程BC必须等到A初始化工作完后才能工作,即线程BC在等待一个条件,我们称BC为等待者。
      当线程A完成初始化工作时,会触发这个条件,那么等待者BC就会被唤醒。触发这个条件的A就是触发者。
      上面的使用场景非常形象,而且条件类提供的函数也非常形象,它的代码如下所示:

       

      system/core/include/utils/Condition.h

      /*

       * Copyright (C) 2007 The Android Open SourceProject

       *

       * Licensed under the Apache License, Version2.0 (the "License");

       * you may not use this file except incompliance with the License.

       * You may obtain a copy of the License at

       *

       *     http://www.apache.org/licenses/LICENSE-2.0

       *

       * Unless required by applicable law or agreedto in writing, software

       * distributed under the License is distributedon an "AS IS" BASIS,

       * WITHOUT WARRANTIES OR CONDITIONS OF ANYKIND, either express or implied.

       * See the License for the specific languagegoverning permissions and

       * limitations under the License.

       */

       

      #ifndef_LIBS_UTILS_CONDITION_H

      #define_LIBS_UTILS_CONDITION_H

       

      #include<stdint.h>

      #include<sys/types.h>

      #include<time.h>

       

      #if defined(HAVE_PTHREADS)

      # include<pthread.h>

      #endif

       

      #include<utils/Errors.h>

      #include<utils/Mutex.h>

      #include<utils/Timers.h>

       

      //---------------------------------------------------------------------------

      namespace android {

      //---------------------------------------------------------------------------

       

      /*

       * Condition variable class. The implementation is system-dependent.

       *

       * Condition variables are paired up withmutexes. Lock the mutex,

       * call wait(), then either re-wait() if thingsaren't quite what you want,

       * or unlock the mutex and continue. All threads calling wait() must

       * use the same mutex for a given Condition.

       */

      class Condition {

      public:

         enum {

             PRIVATE = 0,

             SHARED = 1

         };

        //两种类型:PRIVATE是进程内部使用的;SHARED是适用于跨进程共享的。

         //如不指定,缺省是PRIVATE的类型。

       

         enum WakeUpType {

             WAKE_UP_ONE = 0,

             WAKE_UP_ALL = 1

         };

       

         Condition(); ////构造函数

         Condition(int type);//构造函数,type就是上面的那两种类型

         ~Condition();//析构

         // Wait on the condition variable.  Lock the mutex before calling.

        //线程BC等待事件,wait这个名字也很形象

         status_t wait(Mutex& mutex);

         // same with relative timeout

         //线程BC的超时等待,BC可以指定等待时间,当超过这个时间,条件却还不满足,则退出等待。

         status_t waitRelative(Mutex& mutex,nsecs_t reltime);

         // Signal the condition variable, allowingexactly one thread to continue.

        //触发者A用来通知条件已经满足,但是BC只有一个会被唤醒

         void signal();

         // Signal the condition variable, allowingone or all threads to continue.

         void signal(WakeUpType type) {

             if (type == WAKE_UP_ONE) {

                 signal();

             } else {

                 broadcast();

             }

         }

         // Signal the condition variable, allowingall threads to continue.

        //触发者A用来通知条件已经满足,所有等待者都会被唤醒。

         void broadcast();

       

      private:

      #ifdefined(HAVE_PTHREADS)

         pthread_cond_t mCond;

      #else

         void*  mState;

      #endif

      };

       

      //---------------------------------------------------------------------------

       

      #ifdefined(HAVE_PTHREADS)

       

      inlineCondition::Condition() {

         pthread_cond_init(&mCond, NULL);

      }

      inlineCondition::Condition(int type) {

         if(type == SHARED) {

             pthread_condattr_t attr;

             pthread_condattr_init(&attr);

             pthread_condattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);

             pthread_cond_init(&mCond,&attr);

             pthread_condattr_destroy(&attr);

         } else {

             pthread_cond_init(&mCond, NULL);

         }

      }

      inlineCondition::~Condition() {

         pthread_cond_destroy(&mCond);

      }

      inline status_tCondition::wait(Mutex& mutex) {

         return -pthread_cond_wait(&mCond,&mutex.mMutex);

      }

      inline status_tCondition::waitRelative(Mutex& mutex, nsecs_t reltime) {

      #ifdefined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)

         struct timespec ts;

         ts.tv_sec = reltime/1000000000;

         ts.tv_nsec = reltime%1000000000;

         return-pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);

      #else //HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE

         struct timespec ts;

      #ifdefined(HAVE_POSIX_CLOCKS)

         clock_gettime(CLOCK_REALTIME, &ts);

      #else //HAVE_POSIX_CLOCKS

         // we don't support the clocks here.

         struct timeval t;

         gettimeofday(&t, NULL);

         ts.tv_sec = t.tv_sec;

         ts.tv_nsec= t.tv_usec*1000;

      #endif //HAVE_POSIX_CLOCKS

         ts.tv_sec += reltime/1000000000;

         ts.tv_nsec+= reltime%1000000000;

         if (ts.tv_nsec >= 1000000000) {

             ts.tv_nsec -= 1000000000;

             ts.tv_sec  += 1;

         }

         return -pthread_cond_timedwait(&mCond,&mutex.mMutex, &ts);

      #endif //HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE

      }

       

      //inline函数

      //signal()broadcast()的实现是凭借调用了Raw APIpthread_cond_signal(&mCond)pthread_cond_broadcast(&mCond)

      //这里要重点说明的是,Condition类必须配合Mutex来使用。

      //在上面的代码中,不论是waitwaitRelativesignal还是broadcast的调用,都放在一个Mutexlockunlock范围中,尤其是waitwaitRelative函数的调用,这是强制性的。

      inline voidCondition::signal() {

         /*

          * POSIX says pthread_cond_signal wakes up"one or more" waiting threads.

          *However bionic follows the glibc guarantee which wakes up "exactlyone"

          * waiting thread.

          *

          * man 3 pthread_cond_signal

          *  pthread_cond_signal restarts one of the threads that are waiting on

          *  the condition variable cond. If no threads are waiting on cond,

          *  nothing happens. If several threads are waiting on cond, exactly one

          *  is restarted, but it is not specified which.

          */

         pthread_cond_signal(&mCond);

      }

      inline voidCondition::broadcast() {

         pthread_cond_broadcast(&mCond);

      }

       

      #endif //HAVE_PTHREADS

       

      //---------------------------------------------------------------------------

      }; // namespaceandroid

      //---------------------------------------------------------------------------

       

      #endif // _LIBS_UTILS_CONDITON_H

       

       

      二、为什么android要封装AutoMutex

       android系统中,死锁是非常严重的,基本都是会引起系统死机,crash,重启的,并且死锁在android系统开发中,也是会经常碰见的。所以我们要尽量避免死锁,android就给我们封装了AutoMutex它充分利用了c++的构造与析构机制,在构造函数中 mLock.lock() 加锁,在析构函数中 mLock.unlock() 解锁。

      对一个需要加锁的函数来说,我们只需要在函数开始处,AutoMutex _l(mLock)就完成了加锁,等函数退出时,这样一个临时变量就会析构,就会解锁。

       

      三、Autolock/AutoMutexCondition的使用

       

    1. Autolock/AutoMutex的使用

      用法比较简单,定义一个局部临时的AutoMutex变量,在该变量定义的地方,构造函数被自动调用,会执行Mutexlock()操作;在该变量作用域结束的地方,析构函数会被自动调用,会执行Mutexunlock操作。
      所以,你只需要在Mutex保护的区域开始的地方定义一个AutoMutex变量即可,即可实现用Mutex对该区域的保护。

       

    2. Condition的使用

      我们看一个android原生的类是怎么使用conditionMutex的。

      这个例子是Thread类的requestExitAndWait,目的是等待工作线程退出,代码如下所示:
      system/core/libutils/Threads.cpp
      status_t Thread::requestExitAndWait()

      {

          Mutex::Autolock _l(mLock);

          if (mThread == getThreadId()) {

              ALOGW(

              "Thread (this=%p): don't callwaitForExit() from this "

              "Thread object's thread. It's aguaranteed deadlock!",

              this);

       

              return WOULD_BLOCK;

          }

         

          mExitPending = true;

       

          while (mRunning == true) {

              mThreadExitedCondition.wait(mLock);//这里wait

          }

          // This next line is probably not neededany more, but is being left for

          // historical reference. Note that eachinterested party will clear flag.

          mExitPending = false;

       

          return mStatus;

      }
      那么,什么时候会触发这个条件呢?是在工作线程退出前。其代码如下所示:
      int Thread::_threadLoop(void* user)

      {

          Thread* const self =static_cast<Thread*>(user);

       

          sp<Thread>strong(self->mHoldSelf);

          wp<Thread> weak(strong);

          self->mHoldSelf.clear();

       

      #ifdef HAVE_ANDROID_OS

          // this is very useful for debugging withgdb

          self->mTid = gettid();

      #endif

       

          bool first = true;

       

          do {

              bool result;

              if (first) {

                  first = false;

                  self->mStatus =self->readyToRun();

                  result = (self->mStatus ==NO_ERROR);

       

                  if (result &&!self->exitPending()) {

                      // Binder threads (and maybeothers) rely on threadLoop

                      // running at least once aftera successful ::readyToRun()

                      // (unless, of course, thethread has already been asked to exit

                      // at that point).

                      // This is because threads areessentially used like this:

                      //   (new ThreadSubclass())->run();

                      // The caller therefore doesnot retain a strong reference to

                      // the thread and the thread wouldsimply disappear after the

                      // successful ::readyToRun()call instead of entering the

                      // threadLoop at least once.

                      result = self->threadLoop();

                  }

              } else {

                  result = self->threadLoop();

              }

       

              // establish a scope for mLock

              {

              Mutex::Autolock _l(self->mLock);

              if (result == false ||self->mExitPending) {

                  self->mExitPending = true;

                  self->mRunning = false;

                  // clear thread ID so thatrequestExitAndWait() does not exit if

                  // called by a new thread using thesame thread ID as this one.

                  self->mThread = thread_id_t(-1);

                  // note that interested observersblocked in requestExitAndWait are

                  // awoken by broadcast, but blockedon mLock until break exits scope

                 self->mThreadExitedCondition.broadcast(); //这里broadcast

                  break;

              }

              }

             

              // Release our strong reference, to leta chance to the thread

              // to die a peaceful death.

              strong.clear();

              // And immediately, re-acquire a strongreference for the next loop

              strong = weak.promote();

          } while(strong != 0);

         

          return 0;

      }

       

       

      通过以上的学习,我们对Autolock/AutoMutexCondition就有个深入的理解,

      在以后android系统看到它们就知道他们的作用,已经怎么样去使用它了,达到写的代码更少的bug。

0 0
原创粉丝点击