Microsoft Windows 平臺中兩種最常用的鎖定方法為 WaitForSingleObject 和 EnterCriticalSection。WaitForSingleObject 是一個過載 Microsoft API,可用于檢查和修改許多不同對象(如事件、作業、互斥體、進程、信號、線程或計時器)的狀態。WaitForSingleObject 的一個不足之處是它會始終獲取內核的鎖定,因此無論是否獲得鎖定,它都會進入特權模式 (環路 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 只有當存在鎖定爭用時,才進入內核。