Microsoft Windows
平台中兩種最常用的鎖定方法為 WaitForSingleObject
和 EnterCriticalSection
。WaitForSingleObject
是一個過載 Microsoft API
,可用於檢查和修改許多不同對象(如事件、作業、互斥體、進程、訊號、線程或計時器)的狀態。Wa
itForSingleObject
的一個不足之處是它會始終擷取核心的鎖定,因此無論是否獲得鎖定,它都會進入特權模式 (
環路 0)
。此 API
還進入 Windows
核心,即使指定的逾時為 0
,亦如此。此鎖定方法的另一不足之處在於,它一次只能處理 64
個嘗試對某個對象進行鎖定的線程。WaitForSingleObject
的優點是它可以全域進行處理,這使得此 API
能夠用於進程間的同步。它還具有為作業系統提供鎖定對象資訊的優勢,從而可以實現公平性及優先順序倒置。
通過對關鍵程式碼片段實施 EnterCriticalSection
和 LeaveCriticalSection API
調用,可以使用 EnterCriticalSection
。此 API
具有 WaitForSingleObject
所不具備的優點,因為只有存在鎖定爭用時,才會進入核心。如果不存在鎖定爭用,則此 API
會擷取使用者空間鎖定,並且在未進入特權模式的情況下返回。如果存在爭用,則此 API
在核心中所採用的路徑將與 WaitForSingleObject
極其相似。
在低爭用的情況下,由於 EnterCriticalSection
不進入核心,因此鎖定開銷非常低。
不足之處是 EnterCriticalSection
無法進行全域處理,因此無法為線程擷取鎖定的順序提供任何保證。EnterCriticalSection
是一種阻塞調用,意味著只有線程獲得對此關鍵區段的存取權限時,該調用才會返回。Windows
引入了
TryEnterCriticalSection
,TryEnterCriticalSection
是一種非阻塞調用,無論獲得鎖定與否都會立即返回。此外,EnterCriticalSection
還允許開發人員使用自旋計數對關鍵區段進行初始化,在回退前線程會按此自旋計數嘗試擷取鎖定。通過使用 API InitializeCriticalSectionAndSpinCount
,完成初始化。自旋計數可以在此調用中進行設定,也可以在註冊表中進行設定,以根據不同作業系統及其相應的線程配量對自旋變更。
如果存在鎖定爭用,則 EnterCriticalSection
和 WaitForSingleObject
都會進入核心。如果實現程度過高,從使用者模式到特權模式的轉換開銷將會非常大。
EnterCriticalSection
和 WaitForSingleObject API
調用在對使用數千個周期的運算進行鎖定時,通常不會影響效能。在這些情況下,鎖定調用本身的開銷不會如此突出。會導致效能降低的情況是粒度鎖定,獲得和釋放此鎖定要花費數百個周期。在這些情況下,使用使用者層級鎖定則非常有益。
為了說明在低爭用的情況下 WaitForSingleObject
調用與 EnterCriticalSection
調用的開銷情況,我們分別在 1
個和 2
個線程上運行了記憶體管理鎖定核心
。在低爭用的情況下,存在加速比 (WaitForSingleObject_Time / EnterCriticalSection_Time)
大約為 5
倍的效能之差。在 2
個線程持續爭用的情況下,使用 EnterCriticalSection
和使用 WaitForSingleObject
之間的差別最小。在低爭用的情況下存在效能差距的原因如下:WaitForSingleObject
在每次調用時都進入核心,而 EnterCriticalSection
只有當存在鎖定爭用時,才進入核心。