【发布时间】:2020-03-31 14:39:55
【问题描述】:
我正在使用 Windows 10 1903、Delphi Rio 10.3.3 和 Indy 10.6.2.5366。
我正在将使用 TServerSocket 的旧 Windows 服务应用程序升级到 Indys TIdTCPServer。主要原因是我必须为应用程序添加 IPv6 支持。
我遇到的问题是 TIdTCPServer 吃线程。对于到 TIdTCPServer 的每个连接,都会分配一个新线程(应该如此),但是当客户端断开连接时,线程不会关闭。
如果我同时连接两个客户端,则 IdTCPServer1.Contexts.LockList.Count = 2 并且 OnExecute 事件会在每个“周期”被触发两次。如果我然后断开一个客户端以便 IdTCPServer1.Contexts.LockList.Count = 1,则 OnExecute 每个周期仅触发一次(也应该如此),但两个线程仍在运行。 (我用windows任务管理器看这个)
我构建了一个简单的测试应用程序,它以几秒钟的间隔随机连接和断开连接,启动了 5 个程序,当达到大约 150 个线程时,我的服务崩溃了。
这是我的 OnExecute 事件:
procedure T#############.IdTCPServer1Execute(AContext: TIdContext);
var
incomestring: string;
begin
try
if AContext.Connection.IOHandler.InputBufferIsEmpty then
begin
AContext.Connection.IOHandler.CheckForDataOnSource(100);
AContext.Connection.IOHandler.CheckForDisconnect;
if AContext.Connection.IOHandler.InputBufferIsEmpty then
Exit;
end;
incomestring:= AContext.Connection.IOHandler.InputBufferAsString(en8bit);
if length(trim(incomestring)) = 0 then
begin
exit;
end;
DataRead(incomestring, AContext); // Send data be processed
except
on E: Exception do
begin
if E is EIdException then
raise // <- re-raise only Indy-specific exceptions end;
else
begin
inc(ErrorCount);
Logg('Error in IdTCPServer1Execute: ' + E.Message, ltError, AContext);
end;
end;
end;
end;
我使用 InputBufferAsString 的原因是我无法控制连接到服务器的客户端。所以最后并不总是有 CR 或 LF(正如 ReadLn 所希望的那样)。而且客户端在断开连接之前会与服务器保持连接数小时。
【问题讨论】:
-
您真的觉得有必要审查表单名称吗?你能做的最好的事情是
T####而不是默认的TForm1? -
这可能是线程池的问题。当线程数接近 150 时,真的没有理由崩溃,除非您的系统资源不足。我认为您的问题出在其他地方。
-
您好,您是否尝试使用连接的 ReadTimeout 属性?
-
默认情况下,Indy 不使用线程池。您必须通过将
TIdSchedulerOfThreadPool分配给TIdTCPServer.Scheduler来显式启用它,否则将使用TIdSchedulerOfThreadDefault,它不会合并。但是你真的打电话给IdTCPServer1.Contexts.LockList()吗?如果是这样,你也打电话给IdTCPServer1.Contexts.UnlockList()吗?我在这段代码中看不到任何会导致线程无法正确终止的内容,除非DataRead()或Logg()正在阻塞线程,或者您将Contexts锁定。所示代码之外的内容。 -
感谢您的回复。是的,每次迭代 LockList() 时,我都会在 finally 子句中调用 UnlockList()。问题似乎出在 DataRead 过程中。我会进一步调查。
标签: multithreading delphi indy delphi-10.3-rio