互斥鎖

在編程中,引入了對象互斥鎖的概念,來保證共享數據操作的完整性。每個對象都對應於一個可稱為“互斥鎖” 的標記,這個標記用來保證在任一時刻,只能有一個執行緒訪問該對象。有靜態方式和動態方式兩種方法創建互斥鎖。互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性,不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。

示例

下面舉例:

在Posix Thread中定義有一套專門用於執行緒同步的mutex函式。

1. 創建和銷毀

有兩種方法創建互斥鎖,靜態方式和動態方式。POSIX定義了一個宏PTHREAD_MUTEX_INITIALIZER來靜態初始化互斥鎖,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads實現中,pthread_mutex_t是一個結構,而PTHREAD_MUTEX_INITIALIZER則是一個結構常量。

動態方式是採用pthread_mutex_init()函式來初始化互斥鎖,API定義如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用於指定互斥鎖屬性(見下),如果為NULL則使用預設屬性。

pthread_mutex_destroy ()用於註銷一個互斥鎖,API定義如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 銷毀一個互斥鎖即意味著釋放它所占用的資源,且要求鎖當前處於開放狀態。由於在Linux中,互斥鎖並不占用任何資源,因此LinuxThreads中的 pthread_mutex_destroy()除了檢查鎖狀態以外(鎖定狀態則返回EBUSY)沒有其他動作。

2. 互斥鎖屬性

互斥鎖的屬性在創建鎖的時候指定,在LinuxThreads實現中僅有一個鎖類型屬性,不同的鎖類型在試圖對一個已經被鎖定的互斥鎖加鎖時表現不同。當前(glibc2.2.3,linuxthreads0.9)有四個值可供選擇:

* PTHREAD_MUTEX_TIMED_NP,這是預設值,也就是普通鎖。當一個執行緒加鎖以後,其餘請求鎖的執行緒將形成一個等待佇列,並在解鎖後按優先權獲得鎖。這種鎖策略保證了資源分配的公平性。

* PTHREAD_MUTEX_RECURSIVE_NP,嵌套鎖,允許同一個執行緒對同一個鎖成功獲得多次,並通過多次unlock解鎖。如果是不同執行緒請求,則在加鎖執行緒解鎖時重新競爭。

* PTHREAD_MUTEX_ERRORCHECK_NP,檢錯鎖,如果同一個執行緒請求同一個鎖,則返回EDEADLK,否則與PTHREAD_MUTEX_TIMED_NP類型動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。

* PTHREAD_MUTEX_ADAPTIVE_NP,適應鎖,動作最簡單的鎖類型,僅等待解鎖後重新競爭。

3.鎖操作

鎖操作主要包括加鎖pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個,不論哪種類型的鎖,都不可能被兩個不同的執行緒同時得到,而必須等待解鎖。對於普通鎖和適應鎖類型,解鎖者可以是同進程內任何執行緒;而檢錯鎖則必須由加鎖者解鎖才有效,否則返回EPERM;對於嵌套鎖,文檔和實現要求必須由加鎖者解鎖,但實驗結果表明並沒有這種限制,這個不同還沒有得到解釋。在同一進程中的執行緒,如果加鎖後沒有解鎖,則任何其他執行緒都無法再獲得鎖。

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被占據時返回EBUSY而不是掛起等待。

4. 其他

POSIX 執行緒鎖機制的Linux實現都不是取消點,因此,延遲取消類型的執行緒不會因收到取消信號而離開加鎖等待。值得注意的是,如果執行緒在加鎖後解鎖前被取消,鎖將永遠保持鎖定狀態,因此如果在關鍵區段內有取消點存在,或者設定了異步取消類型,則必須在退出回調函式中解鎖。

這個鎖機制同時也不是異步信號安全的,也就是說,不應該在信號處理過程中使用互斥鎖,否則容易造成死鎖。

互斥鎖屬性使用互斥鎖(互斥)可以使執行緒按順序執行。通常,互斥鎖通過確保一次只有一個執行緒執行代碼的臨界段來同步多個執行緒。互斥鎖還可以保護單執行緒代碼。

要更改預設的互斥鎖屬性,可以對屬性對象進行聲明和初始化。通常,互斥鎖屬性會設定在應用程式開頭的某個位置,以便可以快速查找和輕鬆修改。表 4–1列出了用來處理互斥鎖屬性的函式。

表 4–1 互斥鎖屬性例程

操作 相關函式說明
初始化互斥鎖屬性對象 pthread_mutexattr_init 語法
銷毀互斥鎖屬性對象 pthread_mutexattr_destroy 語法
設定互斥鎖範圍 pthread_mutexattr_setpshared 語法
獲取互斥鎖範圍 pthread_mutexattr_getpshared 語法
設定互斥鎖的類型屬性 pthread_mutexattr_settype 語法
獲取互斥鎖的類型屬性 pthread_mutexattr_gettype 語法
設定互斥鎖屬性的協定 pthread_mutexattr_setprotocol 語法
獲取互斥鎖屬性的協定 pthread_mutexattr_getprotocol 語法
設定互斥鎖屬性的優先權上限 pthread_mutexattr_setprioceiling 語法
獲取互斥鎖屬性的優先權上限 pthread_mutexattr_getprioceiling 語法
設定互斥鎖的優先權上限 pthread_mutex_setprioceiling 語法
獲取互斥鎖的優先權上限 pthread_mutex_getprioceiling 語法
設定互斥鎖的強健屬性 pthread_mutexattr_setrobust_np 語法
獲取互斥鎖的強健屬性 pthread_mutexattr_getrobust_np 語法

表 4–2中顯示了在定義互斥範圍時 Solaris 執行緒和 POSIX 執行緒之間的差異。

表 4–2 互斥鎖範圍比較

Solaris POSIX 定義
USYNC_PROCESS PTHREAD_PROCESS_SHARED 用於同步該進程和其他進程中的執行緒
USYNC_PROCESS_ROBUST 無 POSIX 等效項 用於在進程間 可靠地 同步執行緒
USYNC_THREAD PTHREAD_PROCESS_PRIVATE 用於僅同步該進程中的執行緒

設定協定

pthread_mutexattr_setprotocol(3C)可用來設定互斥鎖屬性對象的協定屬性。

pthread_mutexattr_setprotocol 語法

#include int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);attr 指示以前調用pthread_mutexattr_init()時創建的互斥鎖屬性對象。

protocol 可定義套用於互斥鎖屬性對象的協定。

pthread.h 中定義的 protocol 可以是以下值之一:PTHREAD_PRIO_NONE、PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT。

PTHREAD_PRIO_NONE

執行緒的優先權和調度不會受到互斥鎖擁有權的影響。

PTHREAD_PRIO_INHERIT

此協定值(如 thrd1)會影響執行緒的優先權和調度。如果更高優先權的執行緒因 thrd1 所擁有的一個或多個互斥鎖而被阻塞,而這些互斥鎖是用 PTHREAD_PRIO_INHERIT 初始化的,則 thrd1 將以高於它的優先權或者所有正在等待這些互斥鎖(這些互斥鎖是 thrd1 指所擁有的互斥鎖)的執行緒的最高優先權運行。

如果 thrd1 因另一個執行緒 (thrd3) 擁有的互斥鎖而被阻塞,則相同的優先權繼承效應會以遞歸方式傳播給 thrd3。

使用 PTHREAD_PRIO_INHERIT 可以避免優先權倒置。低優先權的執行緒持有較高優先權執行緒所需的鎖時,便會發生優先權倒置。只有在較低優先權的執行緒釋放該鎖之後,較高優先權的執行緒才能繼續使用該鎖。設定 PTHREAD_PRIO_INHERIT,以便按與預期的優先權相反的優先權處理每個執行緒。

如果為使用協定屬性值 PTHREAD_PRIO_INHERIT 初始化的互斥鎖定義了 _POSIX_THREAD_PRIO_INHERIT,則互斥鎖的屬主失敗時會執行以下操作。屬主失敗時的行為取決於pthread_mutexattr_setrobust_np()的 robustness 參數的值。

解除鎖定互斥鎖。

互斥鎖的下一個屬主將獲取該互斥鎖,並返回錯誤 EOWNERDEAD。

互斥鎖的下一個屬主會嘗試使該互斥鎖所保護的狀態一致。如果上一個屬主失敗,則狀態可能會不一致。如果屬主成功使狀態保持一致,則可針對該互斥鎖調用pthread_mutex_init()並解除鎖定該互斥鎖。

注 –如果針對以前初始化的但尚未銷毀的互斥鎖調用pthread_mutex_init(),則該互斥鎖不會重新初始化。

如果屬主無法使狀態保持一致,請勿調用pthread_mutex_init(),而是解除鎖定該互斥鎖。在這種情況下,所有等待的執行緒都將被喚醒。以後對pthread_mutex_lock()的所有調用將無法獲取互斥鎖,並將返回錯誤代碼 ENOTRECOVERABLE。現在,通過調用pthread_mutex_destroy()來取消初始化該互斥鎖,即可使其狀態保持一致。調用pthread_mutex_init()可重新初始化互斥鎖。

如果已獲取該鎖的執行緒失敗並返回 EOWNERDEAD,則下一個屬主將獲取該鎖及錯誤代碼 EOWNERDEAD。

PTHREAD_PRIO_PROTECT

當執行緒擁有一個或多個使用 PTHREAD_PRIO_PROTECT 初始化的互斥鎖時,此協定值會影響其他執行緒(如 thrd2)的優先權和調度。thrd2 以其較高的優先權或者以 thrd2 擁有的所有互斥鎖的最高優先權上限運行。基於被 thrd2 擁有的任一互斥鎖阻塞的較高優先權執行緒對於 thrd2 的調度沒有任何影響。

如果某個執行緒調用sched_setparam()來更改初始優先權,則調度程式不會採用新優先權將該執行緒移到調度佇列末尾。

執行緒擁有使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥鎖

執行緒解除鎖定使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥鎖

一個執行緒可以同時擁有多個混合使用 PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 初始化的互斥鎖。在這種情況下,該執行緒將以通過其中任一協定獲取的最高優先權執行。

pthread_mutexattr_setprotocol 返回值

如果成功完成,pthread_mutexattr_setprotocol()會返回 0。其他任何返回值都表示出現了錯誤。

如果出現以下任一情況,pthread_mutexattr_setprotocol()將失敗並返回對應的值。

ENOSYS

描述:選項 _POSIX_THREAD_PRIO_INHERIT 和 _POSIX_THREAD_PRIO_PROTECT 均未定義並且該實現不支持此函式。

ENOTSUP

描述:protocol 指定的值不受支持。

如果出現以下任一情況,pthread_mutexattr_setprotocol()可能會失敗並返回對應的值。

EINVAL

描述:attr 或 protocol 指定的值無效。

EPERM

描述:調用方無權執行該操作。

相關詞條

相關搜尋

熱門詞條

聯絡我們