2012-03-26 73 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可以解决这个问题,代码的代价就是简单地停止它的运行,而不考虑任何资源释放或其他线程的行为。