最近在網上看到一些關於在DllMain中不當操作導致死鎖的問題,也沒找到比較確切的解答,這極大吸引了我研究這個問題的興趣。我花了一點時間研究了下,正好也趁機研究了下進程對DllMain的調用規律。因爲整個研究篇幅比較長,我覺得還是分開寫比較能突出重點。本文先說說死鎖。
介紹死鎖之前,我說一個我小時候聽過的一個故事:
某國際實驗機構將在全球各著名小學做個團隊合作的實驗。他們在中國選定了一個“被安排的”學校,然後“隨機”選出一些學生,讓這些學生作爲實驗的樣本參與中國區的實驗。實驗是這樣的:他們在N根細繩一頭捆着一支短粉筆,將這些粉筆放到一個細口瓶中。該瓶口只能容一支粉筆自由出入。然後繩的另一頭放在學生的手中,告知他們要迅速將自己手中繩子捆住的粉筆從瓶子中拽出來。中國學生經過討論後,決定出他們的方案。於是123之後,“聰明謙讓”的中國學生“一個個”並迅速的將各自的粉筆拽了出來。而同樣的實驗,在“苦大仇深”的外國學生中結果卻不理想。因爲他們同時一起往外拽繩子,導致所有的粉筆都卡在瓶口……
這個故事影響了我很久,我一直在思考:外國人這麼笨麼?但是現在我回憶這個故事,卻想到了這個實驗中發生的一些現象和我們在編程中遇到的一些問題是如此的類似。想想,“中國學生”的思路就是“序列化執行”,而外國學生的現象就是因爲“競爭”而導致了“死鎖”。
回到正題,我想熟悉計算機的同學應該對“死鎖”這個概念並不陌生。我們看一下wiki對Deadlock這個詞的解釋:
- A deadlock is a situation in which two or more competing actionsare each waiting for the other to finish, and thus neither ever does。
也就是說:多個操作相互等待其他結束從而導致它們都無法結束的一種場景。爲簡單描述,我以兩個相互影響因素來描述死鎖。
上圖中紅色的部分就是故事中“所有粉筆卡在瓶口”那個糾結的時期。於是左右兩個例程都糾結於此,不再往下執行。
以下我列出比較典型的死鎖案例
- // A線程中 hEventA未激活
- WaitforSingleObject(hEventA, <strong>INFINITE</strong>);
- SetEvent(hEventB);
- // B線程中 hEventB未激活
- WaitforSingleObject(hEventB, <strong>INFINITE</strong>);
- SetEvent(hEventA);
我們再看一個教科書式的死鎖案例
- // A線程
- EnterCriticalSection(&g_csA); //要進入臨界區g_csA
- FunA1(); //該函數不影響死鎖這個必然的結果,只是如果這個函數執行的消耗的時間很完美,將導致死鎖出現的概率大增
- EnterCriticalSection(&g_csB); //要進入臨界區g_csB
- FunA2();
- LeaveCriticalSection(&g_csB); //要退出臨界區g_csB
- LeaveCriticalSection(&g_csA); //要退出臨界區g_csA
- // B線程
- EnterCriticalSection(&g_csB); //要進入臨界區g_csB
- FunB1(); //該函數不影響死鎖這個必然的結果,只是如果這個函數執行的消耗的時間很完美,將導致死鎖出現的概率大增
- EnterCriticalSection(&g_csA); //要進入臨界區g_csA
- FunB2();
- LeaveCriticalSection(&g_csA); //要退出臨界區g_csA
- LeaveCriticalSection(&g_csB); //要退出臨界區g_csB
請大家記住這兩個例子,我們會在之後分析的DllMain中不當操作導致死鎖的案例中再次看到它們的身影。