2012-03-26 94 views
0

我需要爲我的項目創建有狀態的ISAPI擴展。我成功地創建了一個包含在TSessionList = class(TObject)中的TSession對象。爲了清理過期會話,我製作了一個清理線程(TThread後代),它定期掃描TSessionList並釋放所有過期的會話。ISAPI擴展TerminateExtension線程死鎖

我在dpr主執行塊中創建了TSessionList和CleanupThread。這很好。但實際上我不確定,在哪裏銷燬CleanupThread。從文檔中我發現ISAPI擴展必須導出TerminateExtension,它在卸載擴展之前被調用。默認Delphi的ISAPI擴展當然會導出這樣一個函數。所以我「重寫它」=導出我的TerminateExtension,釋放我的會話對象,然後調用默認的ISAPIAPP.TerminateExtensionProc。

這是它的樣子:

function TerminateExtension(dwFlags: DWORD): BOOL; stdcall; 
begin 
    DoneSessions; 
    Result:= ISAPIApp.TerminateExtension(dwFlags); 
end; 

exports 
    GetExtensionVersion, 
    HttpExtensionProc, 
    TerminateExtension; 

begin 
    CoInitFlags := COINIT_MULTITHREADED; 
    Application.Initialize; 
    InitSessions; 
    Application.CreateForm(TSOAPWebModule, SOAPWebModule); 
    Application.Run; 
end. 

的CleanupThread破壞在DoneSessions做這種方式:

begin 
    CleanupThread.Free; 
    SessionList.Free; 
end; 

的CleanupThread是TThread類的簡單的後裔,所以不看任何東西具體在其銷燬代碼中。

問題是TerminateExtension僅在CleanupThread.Free中凍結。進一步調試我發現凍結髮生在TThread.WaitFor中。我懷疑必須存在某種線程死鎖= ISAPI工作線程正在等待我的擴展終止,這會在TThread.WaitFor中等待主線程發出信號(或其他)。

我知道我可以克服這種情況調用CleanupThread.Terminate,然後使用直接WaitForSingleObject(或多個???),並最終釋放它。但這聽起來有點......非標準。

因此,我的問題是:我應該如何以及何時釋放(Terminate - WaitFor - Destroy)ISAPI擴展中的任何支持線程以避免線程死鎖?

順便說一句:我已經在標準的DLL中找到了相同的東西。如果你把任何線程.WaitFor在DLL卸載過程中,你的主要應用程序凍結只是在庫卸載。所以同樣的問題/答案有希望適用於此。

回答

0

例如,您應該嘗試在免費調用之前嘗試發信號通知線程終止;

CleanupThread.Terminate; 
if CleanupThread.Waitfor(60000)<>WR_Abandoned then //wait for 60sec for cleanup 
    CleanupThread.free 
else 
    //do something sensible on timeout or error 

但是,不知道清理線程正在嘗試做什麼,很難說什麼可能導致死鎖。通常這些都是競爭條件的結果,特別是如果終止等待超時,所以您需要指定線程正在執行的操作。作爲hack(並不是多線程的良好習慣),你可以使用winapi調用TERMINATETHREAD(在Windows單元中)強制一個線程退出;

TerminateThread(CleanupThread.handle,0); 

由於這迫使線程立即退出,但同時也意味着沒有線程執行清理 - 記住,調用TTHREAD.TERMINATE並不能保證一個線程將退出 - 這完全取決於你的線程代碼,如果有東西阻塞你的線程,那麼它不會以正常的方式終止。 TerminateThread可以解決這個問題,代碼的代價就是簡單地停止它的運行,而不考慮任何資源釋放或其他線程的行爲。