如何判斷代碼是否處于臨界區(qū)
有這么個(gè)問題
有個(gè)讀者問:當(dāng)異常發(fā)生的時(shí)候,如果不知道是否要清理臨界區(qū)對(duì)象,什么是合適的異常處理方式?
這個(gè)讀者的問題是這樣的:
- “我正在使用SEH,在代碼中我使用了__try/__except塊來保護(hù)那些進(jìn)入和退出臨界區(qū)的代碼。當(dāng)異常發(fā)生的時(shí)候,我不知道我是否還在臨界區(qū)中還是已經(jīng)出來了。就算是使用__try/__finally這種方式,也沒能解決我的
問題。”
問題答案:你可以知道你是否擁有臨界區(qū)對(duì)象,因?yàn)槟阋坏┻M(jìn)入了,就表示你獲取到了臨界區(qū)對(duì)象。
方法1:通過指令指針進(jìn)行判斷
“如果代碼執(zhí)行到這里,則表示我肯定在臨界區(qū)里。”

請(qǐng)注意,以上這種方法對(duì)于嵌套調(diào)用EnterCriticalSection的情況也是能正常工作的。
如果你再次獲取到了臨界區(qū),則一定要記得將調(diào)用包含在它自身的try/finally結(jié)構(gòu)中。
方法2:通過棧變量進(jìn)行判斷
“我記得我是否已經(jīng)進(jìn)入臨界區(qū)”

以上方法也是可以適用于嵌套調(diào)用EnterCriticalSection的情況。只是要記得,每次獲取臨界區(qū)對(duì)象,都要對(duì)cEntered進(jìn)行加1。
方法3:通過對(duì)象來進(jìn)行追蹤
將CRITICAL_SECTION封裝到一個(gè)對(duì)象中。
這種方法最適合上面那位讀者的情況。

請(qǐng)注意,上面的代碼,不適用于嵌套臨界區(qū)的情況。如果你試著獲取臨界區(qū)對(duì)象兩次,則異常處理器只會(huì)退出臨界區(qū)一次。
還請(qǐng)注意,我們認(rèn)為臨界區(qū)在初始狀態(tài)下為未獲取狀態(tài)并添加了斷言。如果恰好已經(jīng)獲得了臨界區(qū),則我們的清理代碼將會(huì)嘗試退出臨界區(qū),但是它實(shí)際并沒有進(jìn)入。(試想一下,如果在第一個(gè)”…”中就出現(xiàn)了異常。)
方法4:通過智能對(duì)象判斷
通過將CRITICAL_SECTION對(duì)象封裝到一個(gè)智能對(duì)象中。
可以在上面的代碼中添加如下方法:DWORD Depth() { return Owned() ? m_dwDepth : 0; }
代碼如下:

當(dāng)異常發(fā)生在臨界區(qū)的時(shí)候進(jìn)行清理會(huì)導(dǎo)致這樣一個(gè)問題:我怎么知道清理操作是安全的?
你使用到了臨界區(qū)說明你想獨(dú)占的使用一個(gè)數(shù)據(jù)結(jié)構(gòu),但是如果在獲取臨界區(qū)的時(shí)候發(fā)現(xiàn)了異常,此時(shí)數(shù)據(jù)結(jié)構(gòu)的狀態(tài)是不穩(wěn)定的。如果只是簡(jiǎn)單的退出臨界區(qū)會(huì)導(dǎo)致數(shù)據(jù)結(jié)構(gòu)進(jìn)入到一個(gè)不一致的狀態(tài),就會(huì)導(dǎo)致一個(gè)很難診斷的問題:為什么我的引用計(jì)數(shù)沒有同步了?
后面我還會(huì)有更多關(guān)于異常的內(nèi)容。
最后
Raymond Chen的《The Old New Thing》是我非常喜歡的博客之一,里面有很多關(guān)于Windows的小知識(shí),對(duì)于廣大Windows平臺(tái)開發(fā)者來說,確實(shí)十分有幫助。