天天看點

Pthread 互斥初始化互斥 對互斥加鎖 對互斥解鎖 Mutex示例 Scoped鎖

初始化互斥

互斥使用 pthread_mutex_t 對象表示。正如 Pthread API 中的絕大多數對象,它表示提供給各種互斥接口的模糊結構。雖然你可以動态建立互斥,絕大多數使用方式都是靜态的:

                pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

上面這段代碼定義和初始化了一個互斥體 mutex。在使用互斥之前,需要做隻有這麼多。

對互斥加鎖

鎖(也稱為擷取)Pthread 互斥是通過 pthread_mutex_lock() 函數實作的:

        #include <pthread.h>         int pthread_mutex_lock (pthread_mutex_t *mutex);

成功調用 pthread_mutex_lock() 會阻塞調用的線程,直到由 mutex 指向的互斥體變得可用。一旦互斥體可用了,調用線程就會被喚醒,函數傳回 0。如果在調用時互斥體可用,函數會立即傳回。

出錯時,函數可能傳回的非 0錯誤碼如下:

EDEADLK   調用線程已經持有請求的互斥體。預設情況下,不會有錯誤碼,嘗試擷取已經持有的互斥體會導緻死                           鎖。 EINVAL       有 mutex 指向的互斥體是非法的。

調用方往往不會檢查傳回值,因為編碼風格良好的代碼不應該在運作時生成錯誤資訊。一種使用方式如下:

        pthread_mutex_lock (&mutex);

對互斥解鎖

加鎖的反面是解鎖,或者稱釋放互斥體。

        #include <pthread.h>         int pthread_mutex_unlock (pthread_mutex_t *mutex);

成功調用 pthread_mutex_unlock() 會釋放由 mutex 所指向的互斥體,并傳回 0。該調用不會阻塞,互斥可以立即釋放。

出錯時,函數傳回非 0 的錯誤碼,可能的錯誤碼如下:

EINVAL      由 mutex 指向的互斥體是無效的。 EPERM     調用程序沒有持有由 mutex 指向的互斥。該錯誤碼是不确定的,如果嘗試釋放一個沒有持有的互斥會産                       生 bug。

對于解鎖,使用者一般也不會檢查傳回值:                  pthread_mutex_unlock (&mutex);

Mutex示例

用銀行取款的問題作為例子:

static pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;

int withdraw (struct account *account, int amount) {      pthread_mutex_lock (&the_mutex);      const int balance = account->balance;      if ( balance < amount)     {           pthread_mutex_unlock (&the_mutex);           return -1;      }      account->balance = balance - amount;      pthread_mutex_unlock (&the_mutex);

     disburse_money (amount);      return 0; }

這個例子使用 pthread_mutex_lock() 來擷取一個互斥體,然後通過 pthread_mutex_unlock() 釋放它。通過這種方式,有助于消除競争條件,但是它引入了銀行中單點競争問題:一次隻能有一個使用者取款!這會帶來很大性能瓶頸,對于一個規模很大的銀行而言,這種方式非常失敗。

是以,絕大多數對鎖的使用都避免全局鎖,而是把鎖和某些資料結構的特定執行個體關聯起來。這稱為細粒度鎖。它可以使得鎖語義更複雜,尤其是對于死鎖避免,但是它是利用現代計算機多核擴充的關鍵。

在這個例子中,不是定義全局鎖,而是在 account結構體内定義一把鎖,使得每個 account 執行個體都有自己的鎖。由于臨界區中的資料隻在 account 結構體中,是以這種機制工作良好。通過隻鎖定借方賬戶,銀行可以并行處理其他客戶的取款操作。

int withdraw (struct account *account, int amount) {      pthread_mutex_lock (&account->mutex);      const int balance = account->balance;      if ( balance < amount)     {           pthread_mutex_unlock (&account->mutex);           return -1;      }      account->balance = balance - amount;      pthread_mutex_unlock (&account->mutex);

     disburse_money (amount);      return 0; }

Scoped鎖

資源擷取及初始化 (RAII)是 C++ 的一種程式設計模式——它是C++語言最強大的模式之一。RAII 通過把資源的生命周期綁定到一個 scoped 對象的生命周期上,高效地實作了資源配置設定和回收。雖然 RAII 本是為了處理異常抛出後的資源清理而設計的,它是管理資源的最強大方式之一。舉個例子,RAII 支援建立一個 “Scpoed 檔案”對象,當建立對象時打開檔案,當對象超出範圍内自動關閉。同樣,我們也可以建立一個 “Scpoed 鎖”,在建立擷取鎖時,當超出作用域空間時,自動釋放互斥體。

#include <pthread.h> #include <boost/noncopyable.h>

class MutexLock : private boost::noncopyable { public:        MutexLock()        {                pthread_mutex_init (&mutex_, NULL);        }        ~MutexLock()        {                pthread_mutex_destory (&mutex_);        }        void lock ()        {              pthread_mutex_lock (&mutex_);        }         void unlock()         {               pthread_mutex_unlock(&mutex_);         }

private:       pthread_mutex_t mutex_; };

class MutexLockGuard : private boost::noncopyable { public:       explicit MutexLockGuard (MutexLock& mutex)               :mutex_(mutex)       {              mutex_.lock();       }       ~MutexLockGuard()       {               mutex_.unlock();        }

private:       MutexLock& mutex_; };