ISAPI扩展TerminateExtension线程死锁

时间:2012-03-26 20:36:42

标签: multithreading delphi isapi

我需要为我的项目创建statefull ISAPI扩展。我成功地创建了一个包含在TSessionList = class(TObject)中的TSession对象。为了清理过期的会话,我做了一个清理线程(TThread后代),它定期扫描TSessionList并释放所有过期的会话。

我在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(或Multiple ???)并最终释放它。但这听起来有点......非标准。

因此,我的问题是:如何以及何时应该释放(终止 - 等待 - 销毁)ISAPI扩展中的任何支持线程以避免线程死锁?

BTW:我已经在标准DLL中发现了相同的内容。如果你在dll卸载过程中放入任何thread.WaitFor,你的主应用程序就会在库卸载中冻结。所以同样的问题/答案希望适用于此。

1 个答案:

答案 0 :(得分:0)

您应该在调用free之前尝试发送线程终止信号,例如;

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

但是,如果不知道清理线程正在尝试做什么,很难说是什么导致死锁。通常这些是竞争条件的结果,特别是如果终止&amp;等待超时,所以你需要指定线程正在做什么。

作为一个hack(并不是多线程的好习惯),您可以使用winapi调用TERMINATETHREAD(在Windows单元中)强制线程退出;

TerminateThread(CleanupThread.handle,0);

因为这会强制立即退出线程,但也意味着没有执行线程清理 - 请记住,调用TTHREAD.TERMINATE并不能保证线程将退出 - 这完全取决于您的线程代码,如果有什么东西阻止了你的线程然后它不会以正常方式终止。 TerminateThread可以解决这个问题,代价是代码简单地停在它的位置而不考虑任何资源释放或者其他线程是什么。