概述 : 保證在某一時刻只有一個線程能訪問資料的簡便辦法。在任意時刻只允許一個線程對共用資源進行訪問。如果有多個線程試圖同時訪問臨界區,那麼在有一個線程進入後其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放後,其他線程可以繼續搶佔,並以此達到用原子方式操作共用資源的目的。
臨界區:在所有同步對象中,臨界區是最容易使用的,但它只能用於同步單個進程中的線程。取得對某個資料區的訪臨界區一次只允許一個線程問權。還有,在這些同步對象中,只有臨界區不是核心對象,它不由作業系統的低級組件管理,而且不能使用控制代碼來操縱,由於不是核心對象,使得它作為一種輕量級的同步機制,同步速度比較快。
使用步驟:
1. 在進程中建立一個臨界區,即在進程中分配一個CRITICAL_SECTION資料結構,該臨界區結構的分配必須是全域的,這樣該進程的不同線程就能訪問它。關於CRITICAL_SECTION結構體的深入分析,可以參見文章:
<<Break Free of Code Deadlocks in Critical Sections Under Windows>>
2. 在使用臨界區同步線程之前,必須調用InitializeCriticalSection來初始化臨界區。在釋放資源之前,只需要初始化一次。
3. VOID EnterCriticalSection:阻塞函數。The function returns when the calling thread is granted ownership。換言之,調用線程不能擷取指定臨界區的所有權時,該線程將睡眠,且在被喚醒之前,系統不會給它分配CPU。或者使用TryEnterCriticalSection方法嘗試進入臨界區,如果進入成功,則調用者線程獲得臨界區的使用權,否則返回失敗。
4. 執行臨界區內的任務
5. BOOL LeaveCriticalSection:非阻塞函數。將當前線程對指定臨界區的引用計數減壹;在使用計數變為零時,另一等待此臨界區的一個線程將被喚醒。
6. 當不需要再使用該臨界區時,使用DeleteCriticalSection來釋放臨界區需要的資源。此函數執行後,再也不能使用EnterCriticalSection和LeaveCriticalSection,除非再次使用InitializeCriticalSection初始化了該臨界區。
注意事項:
1. 臨界區一次只允許一個線程訪問,每個線程必須在視圖操作臨界地區資料之前調用該臨界地區標誌(即一個CRITICAL_SECTION全域變 量)EnterCriticalSection後,其它想要獲得訪問權的線程都會置於睡眠狀態,且在被喚醒以前,系統將停止為它們分配CPU時間片。換言之,臨界區可以且僅可被一個線程擁有,當然,沒有任何線程調用EnterCriticalSection或TryEnterCriticalSection時,臨界區不屬於任何 一個線程。
2. 當擁有臨界區所有權的線程調用LeaveCriticalSection放棄所有權時,系統只喚醒等待隊列中的一個線程,給它所有權,其它線程則繼續等待。
3. 注意,擁有該臨界區的線程,每一次針對此臨界區的EnterCriticalSection調用都會成功(這裡指的是重複調用也會立即返回,也就是支援嵌套調用),且會使得臨界區標誌(即一個CRITICAL_SECTION全域變數)的引用計數增加1。在另一個線程能夠擁有該臨界區之前,擁有它的線程必須調用LeaveCriticalSection足夠多次,在引用計數降為零後,另一線程才有可能擁有該臨界區。換言之,在一個正常使用臨界區的線程中,calSection和LeaveCriticalSection應該成對使用。
4. TryEnterCriticalSection
BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
從函式宣告便可看出端倪,EnterCriticalSection函數的傳回值為VOID,而這裡為BOOL。可見對於TryEnterCriticalSection的調用,需要我們判斷其傳回值。在調用TryEnterCriticalSection時,如果指定的臨界區沒有被任何線程(或還沒有被任何調用線程)擁有,該函數將臨界區的訪問權給予調用的線程,並返回TRUE;不過,如果臨界區已經被另一個線程擁有,它立刻返回FALSE值。TryEnterCriticalSection和EnterCriticalSection之間的最大區別在於TryEnterCriticalSection從來不掛起線程。