1. 程式人生 > >C++易混知識點 6: 如何通過生命週期使用mutex互斥鎖自動加鎖和釋放

C++易混知識點 6: 如何通過生命週期使用mutex互斥鎖自動加鎖和釋放

在涉及到多執行緒操作的時候,對於那些靜態變數我們一定要加以保護。我們通常會使用到的是互斥鎖,通過對鎖的獲取和釋放來保證始終只有一個執行緒對關鍵資料進行操作,但這樣帶來的問題也是很直接的: 繁瑣的操作。

1. 首先我們先定義一個互斥鎖的類,

class EfMutex
{
  public:
    EfMutex(int type = PTHREAD_MUTEX_RECURSIVE) : _type(type), _owner(0), _count(0)
    {
        minit();
    }

    virtual ~EfMutex(void)
    {
        pthread_mutex_destroy(&_mutex);
        return ;
    }

    bool mtake(void)
   {
      int nStatus = pthread_mutex_lock(&_mutex);
      if(0 != nStatus)
      {
         char cMessage[1024] = "Uninitialized";
         std::cerr << __PRETTY_FUNCTION__ <<": (" << nStatus << ") "
                   << strerror_r(nStatus, cMessage, sizeof(cMessage))
                   <<std::endl;
         assert(false);
      }
      else
      {
         _owner = pthread_self();
         _count++;
      }

      return true;
   }

    bool mTryTake(void)
    {
        bool r = (pthread_mutex_trylock(&_mutex) == 0);
        if (r)
        {
            _owner = pthread_self();
            _count++;
        }
        return r;
    }

    bool mgive(void)                                                                                                                   { 

    if (_owner && (_owner != pthread_self()))
    {
        Str msg = "EfMutex::mgive: non-owning thread ";
        msg += Str(pthread_self()) + " attempting to unlock mutex currently locked by " + Str(_owner);
        cerr << msg << endl;
        Callstack::trace(cerr);
        cerr.flush();
        throw strdup(msg.c_str());
    }


    if (!_owner)
    {
        cerr << "EfMutex::mgive: owner not set for this mutex!" << endl;
        Callstack::trace(cerr);
   cerr.flush();
        return false;
    }


    if (!_count)
    {
        cerr << "EfMutex::mgive: mutex count zero!" << endl;
        return false;
    }


    _count--;
    if (_count == 0) _owner = 0;


    bool r = (pthread_mutex_unlock(&_mutex) == 0);
    if (!r)
    {
        //
        // no unlock!
        //
        _count++;
        _owner = pthread_self();
        assert(false);
    }
    return r;                                                                                                                          }

  protected:
    virtual void minit(void)
    {
        pthread_once(&_attrInit, &EfMutex::setupAttrs);
        if (_type == PTHREAD_MUTEX_RECURSIVE)
            pthread_mutex_init(&_mutex, &_recurattr);
        else
            pthread_mutex_init(&_mutex, &_stdattr);

        return ;
    }



  private:
    EfMutex(const EfMutex& src);            // no copying
    EfMutex& operator=(const EfMutex& src); // no copying

    pthread_mutex_t     _mutex;
    int                 _type; // PTHREAD_MUTEX_RECURSIVE or ?
    pthread_t           _owner; // if taken, who took me.
    unsigned            _count;

    //
    // shared thread attributes for EfMutex _mutex initialization.
    //
    static void setupAttrs(void)                                                                                                       {                                                                                                                                    pthread_mutexattr_init(&_stdattr);
      pthread_mutexattr_settype(&_stdattr, PTHREAD_MUTEX_ERRORCHECK);
      pthread_mutexattr_init(&_recurattr);
      pthread_mutexattr_settype(&_recurattr, PTHREAD_MUTEX_RECURSIVE);
      return ;                                                                                                                         }
    static pthread_once_t      _attrInit;
    static pthread_mutexattr_t _stdattr;
    static pthread_mutexattr_t _recurattr;

    friend class EfCondition;

};
     
.cc 檔案
pthread_once_t      EfMutex::_attrInit = PTHREAD_ONCE_INIT;
pthread_mutexattr_t EfMutex::_recurattr;
pthread_mutexattr_t EfMutex::_stdattr;

2. 同樣的,基於上面互斥鎖的定義,我們再利用臨時物件的生命週期來實現鎖的自動加鎖和釋放,

詳細邏輯參考程式碼實現:

// EfMutexHolder is a helper class intended to be used inside of functions
// with several possible return points. Near the top of the function, create
// an EfMutexHolder in local memory (stack). This takes the mutex. When the
// function exits the compiler will have the mutex holder deleted which will
// give the mutex.
//
class EfMutexHolder
{
  public:
    EfMutexHolder(EfMutex& mutex) : _m(mutex), _lockCount(0)
    {
      EfMutexHolder::lock();
    }

    ~EfMutexHolder(void)
    {
      if(_lockCount > 0)
      {
         EfMutexHolder::unlock();
         assert(0 == _lockCount);
      }
      else
         ;

           //_m.mgive();
    }

    void lock()
    {
      _m.mtake();
      ++_lockCount;
    }

    void unlock()
    {
      //Relaxed implementation.  This will forgive the case where the underlying
      //mutex is released by calling mgive on the underlying mutex and bypassing
      //EfMutexHolder
      _m.mgive();
      --_lockCount;                                                                                                                    }                                                                                                                                  

  private:
    EfMutex& _m;
    int _lockCount;


    EfMutexHolder(const EfMutexHolder& src);            // no copying
    EfMutexHolder& operator=(const EfMutexHolder& src); // no assignment                                                           };    


總結,通過物件變數的生命週期來實現對一段程式碼的鎖保護。