本文使用 C++ RAII 機制來封裝互斥量、條件變量,使其自動管理互斥量、條件變量的生命週期,避免手動維護帶來的資源泄露等各類問題。本文使用的是 Linux 下 Pthread 庫。c++
首先封裝 mutex,下面爲實現:segmentfault
class MutexLock : noncopyable { public: MutexLock() { assert(pthread_mutex_init(&mutex_, nullptr) == 0); } ~MutexLock() { assert(pthread_mutex_destroy(&mutex_) == 0); } /** * 加鎖(僅供 MutexLockGuard 調用,嚴禁用戶調用) */ void lock() { pthread_mutex_lock(&mutex_); } /** * 解鎖(僅供 MutexLockGuard 調用,嚴禁用戶調用) */ void unlock() { pthread_mutex_unlock(&mutex_); } /** * 獲取互斥量原始指針(僅供 Condition 調用,嚴禁用戶調用) * 僅供 Condition 調用 * @return 互斥量原始指針 */ pthread_mutex_t* getPthreadMutexPtr() { return &mutex_; } private: pthread_mutex_t mutex_{}; // 互斥量 };
如今MutexLock
對象已經能夠自動管理 mutex 了,它在構造函數中初始化 mutex,在析構函數中銷燬 mutex。多線程
MutexLock
對象有一點不足的是,通常加鎖和解鎖是成對出現的,可是使用 MutexLock
對象的時候,須要本身手動進行加鎖和解鎖。這個問題比較容易解決,引入一個 MutexLockGuard
對象來管理加鎖和解鎖便可。實現以下:函數
class MutexLockGuard : noncopyable { public: explicit MutexLockGuard(MutexLock &mutex) : mutex_(mutex) { mutex_.lock(); } ~MutexLockGuard() { mutex_.unlock(); } private: MutexLock &mutex_; };
MutexLockGuard
對象使用也比較簡單。線程
{ // mutex 爲 MutexLock 對象 MutexLockGuard lock(mutex); ... }
當 MutexLockGuard
對象建立的時候,加鎖。當 MutexLockGuard
對象離開做用域時,MutexLockGuard
對象執行析構函數,解鎖。有了 MutexLockGuard
對象,就不須要手動進行加鎖和解鎖了。指針
MutexLockGuard
對象還有一個小問題:MutexLockGuard
的臨時對象能不能達到咱們想要的自動加鎖和解鎖的效果呢?code
{ // mutex 爲 MutexLock 對象 MutexLockGuard(mutex); // MutexLockGuard 對象已被銷燬 ... }
答案是能自動加鎖和解鎖,但不能保護臨界區。臨時對象在建立後會當即被銷燬,並非在離開做用域的時候被銷燬,因此臨時對象必不能達到鎖住資源的效果。咱們能夠定義一個宏來阻止臨時對象的使用。對象
#define MutexLockGuard(x) error "Missing guard object name"
這樣就完成了對互斥量的封裝了。繼承
有了前面的互斥量的封裝,分裝條件變量就簡單多了。直接看代碼:生命週期
class Condition : noncopyable { public: /** * 構造函數 * @param mutex 互斥量 */ explicit Condition(MutexLock &mutex) : mutex_(mutex) { assert(pthread_cond_init(&cond_, nullptr) == 0); } ~Condition() { assert(pthread_cond_destroy(&cond_) == 0); } void wait() { pthread_cond_wait(&cond_, mutex_.getPthreadMutexPtr()); } /** * 等待規定時間 * @param second 等待的時間 * @return 若是超時,則返回true;不然,返回false */ bool waitForSecond(int second) { struct timespec timeout{}; clock_getres(CLOCK_REALTIME, &timeout); timeout.tv_sec += second; return pthread_cond_timedwait(&cond_, mutex_.getPthreadMutexPtr(), &timeout) == ETIMEDOUT; } void notify() { pthread_cond_signal(&cond_); } void notifyAll() { pthread_cond_broadcast(&cond_); } private: MutexLock &mutex_; pthread_cond_t cond_{}; };
由於條件變量要配合互斥量使用,須要獲取互斥量的指針,因此 MutexLock
對象提供了獲取互斥量指針的 getPthreadMutexPtr
成員函數。 C++ RAII機制不提倡傳遞裸指針,由於這樣作會有很大的風險。正如《Effective C++》第三版條款 15 中說到,這個世界並不完美,不少 APIs 直接涉及資源,例如 Unix 系統 API。因此說,這是無奈之舉。
若是你留意的話,應該注意到前面的三個對象都繼承了 noncopyable
對象,由於他們都是對象語義的,是不可拷貝的。可參考C++ noncopyable類。該實現只是簡單的實現,還有不少問題沒有考慮(例如,未考慮多線程的狀況),徹底沒有達到工業強度。