Delphi 如何解决在DLL的入口函数中创建或结束线程时卡死
先看一下使用Delphi開發DLL時如何使用MAIN函數,
通常情況下并不會使用到DLL的MAIN函數,因為delphi的框架已經把Main函數隱藏起來
而工程函數的 begin ?end 默認就是MAIN函數的DLL_PROCESS_ATTACH事件的處理代碼,如需要完整的處理其他事件,
如?DLL_PROCESS_DETACH,DLL_THREAD_ATTACH,?DLL_THREAD_DETACH,可在工程文件中做如下處理:
procedure DLLEntryPoint(Reason:DWord); begincase Reason ofDLL_PROCESS_ATTACH:StartMyThreadsAndWaitBegin(); // 創建并等待線程開始,這樣會導致卡死 DLL_PROCESS_DETACH: StopMyThreadsAndWaitEnd(); // 停止并等待線程結束(或直接結束進程),這樣會導致卡死 DLL_THREAD_ATTACH:; DLL_THREAD_DETACH:;end; end;beginDllProc := @DLLEntryPoint;DLLEntryPoint(DLL_PROCESS_ATTACH); end.?
其中 DllProc 是SysInit中的全局變量,可簡單理解為保存DLL Entry Point入口函數的地址(實際上RTL內部還有InitLib 和StartLib函數,由編譯器自動處理)。
以上都是題外話,本文主要說明在DLL入口函數里面創建和退出線程為什么卡死和如何解決的問題。
1)在?DLL_PROCESS_ATTACH 事件中 創建線程 出現卡死的問題
? ? 通常情況下在這事件中僅僅是創建并喚醒線程,是不會卡死的,但如果同時有等待線程正式執行的代碼,則會卡死,因為在該事件中,任何啟動的線程都會由于LdrLoadDll中的LdrpLoaderLock 進入鎖定狀態而處于等待,無法進入線程函數,所以也就永遠無法檢測到正式執行的機會。
LdrpLoaderLock是系統的PE Loader的一個重要鎖,保證系統資源的安全,而DLL 入口函數是在PE Loader 結束前執行的,LdrInitializeThunk等函數處理PE 映像 到內存中的過程中,LdrpLoaderLock是處于鎖定狀態的。
? ? 所以解決辦法就是 在?DLL_PROCESS_ATTACH 事件中,僅創建并喚醒線程即可(此時即使是喚醒了,線程也是處理等待狀態),線程函數會在DLL_PROCESS_ATTACH事件結束后才正式執行(實際上如果是通過LoadLibrary加載DLL,則會在LoadLibrary結束前后的某一時刻正式執行)。
2)在DLL_PROCESS_DETACH中結束線程出現卡死的問題
? ?同樣的原因,該事件是調用LdrUnloadDll中執行的,LdrpLoaderLock仍然是鎖定狀態的,而結束線程最終會調用LdrShutdownThread,均會釋放PE Loader所維護的系統內部的共同資源(包括PEB 和TEB等模塊信息和線程TLS數據等),此類共同資源剛好都是使用LdrpLoaderLock進行同步,所以在DLL_PROCESS_DETACH中調用ExitThread->LdrShutdownThread,必然導致卡死。
? ? ? 另外有一個特殊的現象,就是DLL_PROCESS_DETACH事件中,線程處于掛起狀態,這是因為系統分配線程執行時間片的過程中由于PE Loader有資源處于鎖定而導致線程無法進行下一個時間片,最終表現為線程函數處于假死狀態,此狀態基本上等同于線程的掛起(suspend)狀態。
? ? ? 解決辦法同樣是避免在?DLL_PROCESS_DETACH事件中結束線程,那么我們可以在該事件中,創建并喚醒另外一個線程,在該新的線程里,結束需要結束的線程,并在完成后結束自身即可。唯一需要注意的是,一旦DLL_PROCESS_DETACH結束,內存中與DLL相關的PE映像資源可能會被釋放掉,所以在后續的操作中盡量不要再對原來的數據進行操作,否則容易導致內存溢出(但其實釋放與否是由內核決定的,也許將來經過某一個版本的補丁后,相關資源仍然會保留在內存可以使用)。
? ? ? 提醒: 標準的做法還是建議遵循MS的規則,不要在DLL入口函數中做線程相關的創建和釋放操作。
?
總體上代碼如下:
procedure DLLEntryPoint(Reason:DWord); begincase Reason ofDLL_PROCESS_ATTACH:TThread.CreateAnonymousThread(procedure beginStartMyThreadsAndWaitBegin();end).Start;DLL_PROCESS_DETACH:TThread.CreateAnonymousThread(procedure beginStopMyThreadsAndWaitEnd();end).Start;DLL_THREAD_ATTACH:; DLL_THREAD_DETACH:;end; end;beginDllProc := @DLLEntryPoint;DLLEntryPoint(DLL_PROCESS_ATTACH); end.?
注: 此問題是屬于系統多線程處理的問題,或者說是屬于Windows API的使用方法問題,使用其他VB VC等開發的人員也可以參考此解決方法。
?
轉載于:https://www.cnblogs.com/caibirdy1985/p/5790309.html
總結
以上是生活随笔為你收集整理的Delphi 如何解决在DLL的入口函数中创建或结束线程时卡死的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 混合图 (Standard IO)
- 下一篇: 洛谷P1546 最短网络 Agri-Ne