線程的同步(三)---互斥

互斥對象(mutex)能夠確保線程擁有對單個資源的互斥訪問權。實際上互斥對象是因此而得名的。互斥對象包含一個使用數量,一個線程I D和一個遞歸計數器。互斥對象的行爲特性與臨界區相同,但是互斥對象屬於內核對象,而臨界區則屬於用戶方式對象。這意味着互斥對象的運行速度比關鍵代碼段要慢。但是這也意味着不同進程中的多個線程能夠訪問單個互斥對象,並且這意味着線程在等待訪問資源時可以設定一個超時值。

只有擁有互斥對象的線程才具有訪問資源的權限,由於互斥對象只有一個,因此就決定了任何情況下此共享資源都不會同時被多個線程所訪問。當前佔據資源的線程在任務處理完後應將擁有的互斥對象交出,以便其他線程在獲得後得以訪問資源。互斥量比臨界區複雜。因爲使用互斥不僅僅能夠在同一應用程序不同線程中實現資源的安全共享,而且可以在不同應用程序的線程之間實現對資源的安全共享。

 

1SDK方式使用互斥

若要使用互斥對象,必須有一個進程首先調用CreateMutex,以便創建互斥對象:

HANDLE CreateMutex(

   PSECURITY_ATTRIBUTES psa,

   BOOL fInitialOwner,

   PCTSTR pszName);

psa參數,關於安全性的,可參見《線程的同步()---事件》。

fInitialOwner參數用於控制互斥對象的初始狀態。如果傳遞FALSE(這是通常情況下傳遞的值),那麼互斥對象的ID和遞歸計數器均被設置爲0,這意味着該互斥對象沒有被任何線程所擁有,即初始化爲有信息狀態。

如果爲fInitialOwner參數傳遞TRUE,那麼該對象的線程ID被設置爲調用線程的ID,遞歸計數器被設置爲1。由於ID是個非0數字,因此該互斥對象初始爲無信號狀態,需要在調用線程中將其設置爲有信號狀態。

另外,通過調用OpenMutex,另一個進程可以獲得它自己進程與現有互斥對象相關的句柄:

HANDLE OpenMutex(

   DWORD fdwAccess,

   BOOL bInheritHandle,

   PCTSTR pszName);

通過調用一個等待函數(比如WaitForSingleObject函數),並傳遞負責保護資源的互斥對象的句柄,線程就能夠獲得對共享資源的訪問權。在內部,等待函數要檢查線程的ID,以瞭解它是否是0(互斥對象爲有信號狀態)。如果線程ID0,那麼該線程ID被設置爲調用線程的ID,遞歸計數器被設置爲1,同時,調用線程保持可調度狀態。

如果等待函數發現ID不是0(互斥對象爲無信號狀態),那麼調用線程便進入等待狀態。系統將記住這個情況,並且在互斥對象的ID重新設置爲0時,將線程ID設置爲等待線程的ID,將遞歸計數器設置爲1,並且允許等待線程再次成爲可調度線程。與所有情況一樣,對互斥內核對象進行的檢查和修改都是以原子操作方式進行的。

對於互斥對象來說,正常的內核對象的已通知和未通知規則存在一個特殊的異常情況。比如說,一個線程試圖等待一個未通知的互斥對象。在這種情況下,該線程通常被置於等待狀態。然而,系統要查看試圖獲取互斥對象的線程的I D是否與互斥對象中記錄的線程I D相同。如果兩個線程ID相同,即使互斥對象處於未通知狀態,系統也允許該線程保持可調度狀態。我們不認爲該異常行爲特性適用於系統中的任何地方的其他內核對象。每當線程成功地等待互斥對象時,該對象的遞歸計數器就遞增。若要使遞歸計數器的值大於1,唯一的方法是線程多次等待相同的互斥對象,以便利用這個異常規則。

一旦線程成功地等待到一個互斥對象,該線程就知道它已經擁有對受保護資源的獨佔訪問權。試圖訪問該資源的任何其他線程(通過等待相同的互斥對象)均被置於等待狀態中。當目前擁有對資源的訪問權的線程不再需要它的訪問權時,它必須調用ReleaseMutex函數來釋放該互斥對象:

BOOL ReleaseMutex(HANDLE hMutex);

該函數將對象的遞歸計數器遞減1。如果線程多次成功地等待一個互斥對象,在互斥對象的遞歸計數器變成0之前,該線程必須以同樣的次數調用ReleaseMutex函數。當遞歸計數器到達0時,該線程ID也被置爲0,同時該對象變爲已通知狀態。

當該對象變爲已通知狀態時,系統要查看是否有任何線程正在等待互斥對象。如果有,系統將按公平原則選定等待線程中的一個,爲它賦予互斥對象的所有權。當然,這意味着線程I D被設置爲選定的線程的ID,並且遞歸計數器被置爲1。如果沒有其他線程正在等待互斥對象,那麼該互斥對象將保持已通知狀態,這樣,等待互斥對象的下一個線程就立即可以得到互斥對象。

 

2、使用MFCCMutex

CMutex的使用比較簡單,同CCriticalSection一樣。

用法一:

         CMutex mutexObj;

 

         mutexObj.Lock();

         ...

         mutexObj.Unlock();

用法二:

         CMutex mutexObj;

         CSingleLock singleLock(&mutexObj);

 

         singleLock.Lock();

         ...

         singleLock.Unlock();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章