考虑以下 SO 问题: 在非 VCL 应用程序中使用同步是否危险?
我想知道在 Apache Web 应用程序 DLL 中使用它们时会受到怎样的影响TThread's Synchronize。CheckSynchronizeWaitFor
当我意识到WaitFor正在锁定/挂起时,我开始对此进行调查。在WaitFor它内部检查if CurrentThread.ThreadID = MainThreadID然后重复检查结果,MsgWaitForMultipleObjects并根据它将要执行的结果,CheckSynchronize或者PeekMessage当它收到 a 时,WAIT_OBJECT_0它最终将退出循环。如果MainThreadID不一样的CurrentThreadID话WaitFor会做单的WaitForSingleObject。
因此,我创建了以下测试 Apache Web 模块 DLL,以查看MainThreadID在 Web 请求的生命周期内是什么。
type
TTheThread = class(TThread)
private
protected
procedure Execute; override;
public
end;
TWebModule1 = class(TWebModule)
procedure WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
procedure WebModuleCreate(Sender: TObject);
procedure WebModuleDestroy(Sender: TObject);
private
public
end;
procedure DoLog(AMsg : String);
var
WebModuleClass: TComponentClass = TWebModule1;
TestThread : TTheThread;
Logger : TLogger;
implementation
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
procedure DoLog(AMsg : String);
begin
AMsg := AMsg + #13#10 + 'MainThreadID: ' + InttoStr(MainThreadID) +
#13#10 + 'CurrThreadID: ' + InttoStr(GetCurrentThreadId);
Logger.Log(ltNormal,AMsg);
end;
procedure TTheThread.Execute;
begin
While not Terminated do
begin
Sleep(5000);
DoLog('Thread Execute - Inside While - Terminated: ' + BoolToStr(Terminated,True));
end;
DoLog('Thread Execute - End - Terminated: ' + BoolToStr(Terminated,True));
end;
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.Content :=
'<html>' +
'<head><title>Web Server Application</title></head>' +
'<body>Web Server Application</body>' +
'</html>';
DoLog('WebModule1DefaultHandlerAction');
end;
procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
Logger := TLogger.Create(nil);
{set some Logger properties}
DoLog('WebModuleCreate - Before Thread Create');
TestThread := TTheThread.Create(True);
TestThread.Start;
DoLog('WebModuleCreate - Created Thread');
end;
procedure TWebModule1.WebModuleDestroy(Sender: TObject);
begin
DoLog('WebModuleDestroy Before Terminate');
TestThread.Terminate;
DoLog('WebModuleDestroy Before WaitFor');
TestThread.WaitFor;
DoLog('WebModuleDestroy Before Free');
TestThread.Free;
DoLog('WebModuleDestroy End');
Logger.Free;
end;
end.
所以我启动 Apache,在浏览器中访问网页,倒计时大约 12 秒,然后我停止 Apache。我给了它一段时间,但该httpd服务只破坏了几个线程,然后保持不变并且永远不会关闭。
这是日志:
2021-03-03 10:23:42 081 WebModuleCreate - Before Thread Create
2021-03-03 10:23:42 081 ^ MainThreadID: 2432
2021-03-03 10:23:42 081 ^ CurrThreadID: 6328
2021-03-03 10:23:42 081 WebModuleCreate - Created Thread
2021-03-03 10:23:42 081 ^ MainThreadID: 2432
2021-03-03 10:23:42 081 ^ CurrThreadID: 6328
2021-03-03 10:23:42 081 WebModule1DefaultHandlerAction
2021-03-03 10:23:42 081 ^ MainThreadID: 2432
2021-03-03 10:23:42 081 ^ CurrThreadID: 6328
2021-03-03 10:23:47 093 Thread Execute - Inside While - Terminated: False
2021-03-03 10:23:47 093 ^ MainThreadID: 2432
2021-03-03 10:23:47 093 ^ CurrThreadID: 2220
2021-03-03 10:23:52 100 Thread Execute - Inside While - Terminated: False
2021-03-03 10:23:52 100 ^ MainThreadID: 2432
2021-03-03 10:23:52 100 ^ CurrThreadID: 2220
2021-03-03 10:23:57 101 Thread Execute - Inside While - Terminated: False
2021-03-03 10:23:57 101 ^ MainThreadID: 2432
2021-03-03 10:23:57 101 ^ CurrThreadID: 2220
2021-03-03 10:23:58 313 WebModuleDestroy Before Terminate
2021-03-03 10:23:58 313 ^ MainThreadID: 2432
2021-03-03 10:23:58 313 ^ CurrThreadID: 2432
2021-03-03 10:23:58 313 WebModuleDestroy Before WaitFor
2021-03-03 10:23:58 313 ^ MainThreadID: 2432
2021-03-03 10:23:58 313 ^ CurrThreadID: 2432
2021-03-03 10:24:02 108 Thread Execute - Inside While - Terminated: True
2021-03-03 10:24:02 108 ^ MainThreadID: 2432
2021-03-03 10:24:02 108 ^ CurrThreadID: 2220
2021-03-03 10:24:02 108 Thread Execute - End - Terminated: True
2021-03-03 10:24:02 108 ^ MainThreadID: 2432
2021-03-03 10:24:02 108 ^ CurrThreadID: 2220
如您所见,WaitFor永远不会结束。有趣的是,OnWebModuleCreate它在与假设不同的线程中运行,MainThreadID但在调用时OnWebModuleDestroy运行在相同MainThreadID的线程中。CurrentThreadID = MainThreadIDWaitFor
所以我怀疑我将永远不会使用Synchronize, 并且WaitFor在这种情况下。这是正确的还是比我看到的更多?
如果根据我的假设,所谓的主线程可能根本没有调用,我可以用什么来进行同步,我CheckSynchronize可以只做一个WaitForSingleObject而不是做一个WaitFor吗?我怀疑这里可能根本不需要同步,因为主线程的意义可能根本不需要。
更新
经过大量阅读后,我发现了有关的信息,DLLMain/DLLProc并且每当进程“附加/分离”并且线程“附加/分离”时都会调用它
Raymond Chen 还发表了一篇博客文章,讨论了DLLMain.
因此,似乎因为DLLMain在卸载 DLL 以及线程退出时调用,所以我无法WaitFor在 DLL 卸载代码 ( OnWebModuleDestroy) 中执行线程,因为这显然会导致死锁,因为一个将等待另一个。如果有人能就此意见提出建议,我将不胜感激。