C++ 封裝互斥物件

KingsLanding發表於2015-03-21

  多執行緒程式中為了防止執行緒併發造成的競態,需要經常使用到Mutex進行資料保護。posix提供了phtread_mutex_t進行互斥保護資料。Mutex的使用需要初始化和釋放對應(phtread_mutex_init() 和 phtread_mutex_destroy() 對應),上鎖和解鎖對應(phtread_mutex_lock 和 pthread_mutex_unlock對應)。lock和unlock的過程是設計邏輯的一部分一般都程式設計師都能正確的進行加鎖和解鎖對應,但是要防止lock之後程式出現異常或者提前return而沒有unlock。初始化mutex之後不釋放也會造成資源洩漏,也是很容易遺漏的地方。在實際開發中一般都需要自己封裝一下Mutex。

class MutexLock 
{
 public:
  MutexLock() : holder_(0)
  {
    int ret = pthread_mutex_init(&mutex_, NULL);
    assert(ret == 0); 
  }

  ~MutexLock()
  {
    assert(holder_ == 0);
    int ret = pthread_mutex_destroy(&mutex_);
    assert(ret == 0); 
  }

  bool isLockedByThisThread() const
  {
    return holder_ == static_cast<pid_t>(::syscall(SYS_gettid));
  }

  void assertLocked() const
  {
    assert(isLockedByThisThread());
  }

  void lock()
  {
    pthread_mutex_lock(&mutex_);
    assignHolder();
  }

  void unlock()
  {
    unassignHolder();
    pthread_mutex_unlock(&mutex_);
  }

  pthread_mutex_t* getPthreadMutex()
  {
    return &mutex_;
  }

 private:
  
  MutexLock(const MutexLock &);
  MutexLock &operator=(const MutexLock &);

  void unassignHolder()
  {
    holder_ = 0;
  }

  void assignHolder()
  {
    holder_ = static_cast<pid_t>(::syscall(SYS_gettid));
  }

  pthread_mutex_t mutex_;
  pid_t holder_;
};


class MutexLockGuard 
{
 public:
  explicit MutexLockGuard(MutexLock& mutex)
    : mutex_(mutex)
  {
    mutex_.lock();
  }

  ~MutexLockGuard()
  {
    mutex_.unlock();
  }

 private:
  MutexLockGuard(const MutexLockGuard &);
  MutexLockGuard &operator=(const MutexLockGuard &);

  MutexLock& mutex_;
};

  為了提高MutextLock的易用性,增加了一個MutexLockGuard 類來封裝MutextLock,實際使用的時候直接使用MutexLockGuard,這樣就能防止忘記釋放Mutex的情況出現(MutexLockGuard 超出作用域(一般是一個棧上變數)就會自動釋放,呼叫解構函式,destroy掉mutex)。

  以上封裝其實就是所謂的RAII的一個具體實踐,C++中的智慧指標shared_ptr,weak_ptr,unique_ptr 也是RAII的優秀實現。

  注:其實在C++11 執行緒庫中已經有lock guard可以直接使用了(std::lock_guard ,只需要include<mutex>),不需要自己再寫一遍,對於沒有遷移到C++11上的專案可以使用自己封裝的Mutex來提高易用性。

 

相關文章