1. 程式人生 > >[經驗總結]呼叫WinSock的closesocket函數出現死鎖的解決辦法

[經驗總結]呼叫WinSock的closesocket函數出現死鎖的解決辦法

       這兩天除錯一個網路應用程式,出現一個很詭異的問題:程式在關閉連線時失去響應。用Process Explorer工具檢視該程式的各個執行緒,發現一個工作執行緒的呼叫棧類似這樣: stopProc ==> closesocket ==> EnterCriticalSection ==> RTEnterCriticalSection .... .... 

       看到CriticalSectoin的相關呼叫,我想到closesocket函式可能遇到了死鎖。一個看似簡單的closesocket還需要加鎖嗎? 帶著這個疑問,我翻出了ReactOS和wine的原始碼。在這兩份程式碼中,socket控制代碼都儲存在核心維護的一個雙向連結串列中,closesocket執行的操作就是將socket控制代碼從連結串列中刪除,這樣當然需要採用同步機制,以保證同一個時刻只有一個執行緒能操作連結串列。closesocket函式採用了關鍵段來同步socket連結串列操作,所以在Process Explorer中能看到EnterCriticalSection呼叫。

       分析到這裡,還是沒能解決問題,到底是什麼原因導致死鎖。我又開始查MSDN,查看了closesocket相關資料,知道了grace shutdown這個“新名詞”,還試了幾個偏方,如設定SO_REUSEADDR屬性,設定shutdown的linger引數,無奈還是沒能解決問題。不過此般學習倒是讓我開始注意了關閉連線時的收發操作。這時,我再次回到自己的程式碼,走查了相關程式碼,發現當closehandle時,另外一個工作執行緒會有收發(recv、send)呼叫,這兩個呼叫很可能是導致closesocket死鎖的“元凶”。為了驗證這個觀點,我用英文關鍵詞Google了一般(之前沒找到有用的中文資訊),發現了這個帖子"

close socket deadlocks blocked threads",雖然帖子中提到的是BSD系統的問題,但其現象我遇到的一樣。所以,到現在,我幾乎可以肯定,死鎖原因就是那個工作執行緒中的收發呼叫。接下來的解決辦法就比較簡單了:在closesocket呼叫之前,關閉所有可能呼叫 recv、send的執行緒。直接關閉執行緒的做法比較粗暴,但在我的程式碼,這樣的修改是可行的。

       總結一下,如果在關閉socket時,有另外的執行緒在利用這個socket進行收發,那麼closesocket可能會死鎖。解決的辦法就是在closesocket呼叫之前,關閉所有的recv、send操作。