區別:1.互斥量用於執行緒的互斥,信號量用於執行緒的同步;2、互斥量值只能為0或1,訊號量值可以為非負整數; 3.互斥量的加鎖和解鎖必須由同一執行緒分別對應使用,訊號量可以由一個執行緒釋放,另一個執行緒得到。
本教學操作環境:windows7系統、Dell G3電腦。
互斥量和信號量的差異
1. 互斥量用於執行緒的互斥,訊號量用於執行緒的同步。
這是互斥量和訊號量的根本區別,也就是互斥和同步之間的區別。
互斥:是指某一資源同時只允許一個訪客對其進行訪問,具有唯一性和排它性。但互斥無法限制訪客對資源的存取順序,即存取是無序的。
同步:是指在互斥的基礎上(大多數情況),透過其它機制實現訪客對資源的有序存取。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪客同時存取資源
2. 互斥量值只能為0/1,訊號量值可以為非負整數。
也就是說,一個互斥量只能用於一個資源的互斥訪問,它不能實現多個資源的多執行緒互斥問題。信號量可以實現多個同類資源的多執行緒互斥和同步。當信號量為單值信號量是,也可以完成一個資源的互斥存取。
3. 互斥量的加鎖和解鎖必須由同一執行緒分別對應使用,訊號量可以由一個執行緒釋放,另一個執行緒得到。
互斥量(Mutex)
互斥量表現互斥現象的資料結構,也被當作二元訊號燈。一個互斥基本上是一個多任務敏感的二元訊號,它能用作同步多任務的行為,它常用作保護從中斷來的臨界段程式碼並且在共享同步使用的資源。
Mutex本質上說就是一把鎖,提供對資源的獨佔訪問,所以Mutex主要的作用是用於互斥。 Mutex物件的值,只有0和1兩個值。這兩個值也分別代表了Mutex的兩種狀態。值為0, 表示鎖定狀態,當前物件被鎖定,用戶進程/執行緒如果試圖Lock臨界資源,則進入排隊等待;值為1,表示空閒狀態,當前物件為空閒,用戶進程/執行緒可以Lock臨界資源,之後Mutex值減1變成0。
Mutex可以抽象化為四個動作:
- 建立Create
#- 加鎖Lock
- 解鎖Unlock
-銷毀Destroy
Mutex建立時可以有初始值,表示Mutex被建立後,是鎖定狀態還是空閒狀態。在同一個執行緒中,為了防止死鎖,系統不允許連續兩次對Mutex加鎖(系統一般會在第二次呼叫立刻返回)。也就是說,加鎖和解鎖這兩個對應的操作,需要在同一個執行緒完成。
不同作業系統中提供的Mutex函數:
#動作/系統 |
Win32 |
Linyx |
Solaris |
建立 | CreateMutex |
pthread_mutex_init |
mutex_init |
#加鎖 |
WaitForSingleObject |
#pthread_mutex_lock |
##mutex_lock
|
#解鎖
|
ReleaseMutex
|
pthread_mutex_unlock
|
mutex_unlock
|
銷毀
|
#CloseHandle
|
pthread_mutex_destroy
|
mutex_destroy
|
死鎖主要發生在有多個依賴鎖存在時, 會在一個線程試圖以與另一個線程相反順序鎖住互斥量時發生. 如何避免死鎖是使用互斥量應該格外注意的東西。
總體來講, 有幾個不成文的基本原則:
對共享資源操作前一定要獲得鎖。
完成作業以後一定要釋放鎖定。
盡量短暫地佔用鎖。
如果有多鎖, 如獲得順序是ABC連環扣, 釋放順序也應該是ABC。
執行緒錯誤返回時應該釋放它所獲得的鎖定。
也許還有讀者好奇,「掛起等待」和「喚醒等待執行緒」的操作如何實現?每個Mutex有一個等待佇列,一個執行緒要在Mutex上掛起等待,先在把自己加入等待佇列中,然後置執行緒狀態為睡眠,然後呼叫調度器函數切換到別的執行緒。一個線程要喚醒等待隊列中的其它線程,只需從等待隊列中取出一項,把它的狀態從睡眠改為就緒,加入就緒隊列,那麼下次調度器函數執行時就有可能切換到被喚醒的線程。
一般情況下,如果同一個執行緒先後兩次呼叫lock,在第二次呼叫時,由於鎖已經被佔用,該執行緒會掛起等待別的執行緒釋放鎖,然而鎖正是被自己佔據著的,該線程又被掛起而沒有機會釋放鎖,因此就永遠處於掛起等待狀態了,這叫做死鎖(Deadlock)。另一種典型的死鎖情形是這樣:線程A獲得了鎖1,線程B獲得了鎖2,這時線程A調用lock試圖獲得鎖2,結果是需要掛起等待線程B釋放鎖2,而這時線程B也呼叫lock試圖獲得鎖1,結果是需要掛起等待線程A釋放鎖1,於是線程A和B都永遠處於掛起狀態了。不難想像,如果涉及到更多的線程和更多的鎖,有沒有可能死鎖的問題將會變得複雜和難以判斷。
信號量
信號量(Semaphore),有時被稱為信號燈,是在多線程環境下使用的一種設施, 它負責協調各個線程,以確保它們能夠正確、合理的使用公共資源。
訊號量可分為幾類:
二進位訊號量(binary semaphore):只允許訊號量取0或1值,同時只能被一個線程獲取。
整數訊號量(integer semaphore):訊號量取值是整數,它可以被多個執行緒同時獲得,直到訊號量的值變成0。
記錄型訊號量(record semaphore):每個訊號量s除一個整數值value(計數)外,還有一個等待佇列List,其中是阻塞在該訊號量的各個線程的標識。當信號量被釋放一個,值被加一後,系統自動從等待佇列中喚醒一個等待中的線程,讓其獲得信號量,同時信號量再減一。
信號量透過一個計數器控制對共享資源的訪問,信號量的值是一個非負整數,所有通過它的執行緒都會將該整數減一。如果計數器大於0,則存取被允許,計數器減1;如果為0,則存取被禁止,所有試圖通過它的執行緒都將處於等待狀態。
計數器計算的結果是允許存取共享資源的通行證。因此,為了存取共享資源,執行緒必須從信號量獲得通行證, 如果該信號量的計數大於0,則此執行緒獲得一個通行證,這將導致信號量的計數遞減,否則,此執行緒將阻塞直到獲得一個通行證為止。當此執行緒不再需要存取共享資源時,它會釋放該通行證,這導致信號量的計數遞增,如果另一個執行緒等待通行證,則該執行緒將在那時獲得通行證。
Semaphore可以抽象化成五個運算:
- 建立Create
-
- 等待Wait:
線程等待信號量,如果值大於0,則獲得,值減一;如果只等於0,則一直線程進入睡眠狀態,知道信號量值大於0或超時。
-
-釋放 Post
執行釋放信號量,則值加一;如果此時有正在等待的線程,則喚醒該線程。
-
-試圖等待TryWait
如果呼叫TryWait,執行緒並不真正的去獲得信號量,還是檢查信號量是否能夠被獲得,如果信號量值大於0,則TryWait返回成功;否則返回失敗。
-銷毀 Destroy
信號量,是可以用來保護兩個或多個關鍵程式碼段,這些關鍵程式碼段不能並發呼叫。在進入一個關鍵程式碼段之前,執行緒必須取得一個信號量。如果關鍵程式碼段中沒有任何線程,那麼線程會立即進入該框圖中的那個部分。一旦該關鍵程式碼段完成了,那麼該執行緒必須釋放信號量。其它想進入該關鍵程式碼段的線程必須等待直到第一個線程釋放信號量。為了完成這個過程,需要建立一個訊號量,然後將Acquire Semaphore VI以及Release Semaphore VI分別放置在每個關鍵程式碼段的首端。確認這些信號量VI引用的是初始建立的信號量。
動作/系統 |
#Win32 |
POSIX |
建立 |
CreateSemaphore |
sem_init |
#等待 |
WaitForSingleObject |
sem _wait |
釋放 |
ReleaseMutex |
#sem _post |
##試圖等待
|
WaitForSingleObject
|
sem _trywait
|
銷毀
|
CloseHandle
|
sem_destroy
|
更多相關知識,請訪問常見問題專欄!
以上是信號量和互斥量的差別是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!