Linux环境下TIdHTTPServer调用Shutdown关闭时挂死问题求助
问题根因补充说明
该问题仅在TIdHTTPServer触发的核心原因是:TIdHTTPServer默认开启HTTP Keep-Alive机制,处理完请求后不会立刻断开连接,工作线程会处于等待新请求的阻塞状态,不会主动退出释放关联Yarn对象;而你此前使用的TIdTCPServer业务逻辑通常是处理完单任务即断开连接,工作线程执行完后会自动释放Yarn,因此关闭时不会触发死循环。
修复方案
方案1:关闭前主动断开所有活跃连接(无需修改Indy源码,推荐优先使用)
在设置IdHTTPServer1.Active := false之前,先手动关闭监听端口、断开所有已建立的连接,触发工作线程主动退出释放Yarn,示例代码如下:
procedure TYourServiceForm.StopHTTPService; var idx: Integer; ctx: TIdContext; begin // 先关闭监听端口,停止接收新连接 IdHTTPServer1.Bindings.Close; // 倒序遍历所有活跃连接上下文主动断开 for idx := IdHTTPServer1.Contexts.Count - 1 downto 0 do begin try ctx := TIdContext(IdHTTPServer1.Contexts.Items[idx]); if Assigned(ctx.Connection) then ctx.Connection.Disconnect; except // 忽略断开过程中可能抛出的连接已断开异常 end; end; // 等待100-500毫秒给线程执行退出逻辑 Sleep(200); // 再执行服务关闭操作 IdHTTPServer1.Active := False; end;
方案2:修改Indy源码修复调度器逻辑(一劳永逸解决同类问题)
找到Indy源码中IdSchedulerOfThread.pas文件的TIdSchedulerOfThread.TerminateYarn方法,补充等待线程退出、主动释放Yarn的逻辑,修改后代码如下:
procedure TIdSchedulerOfThread.TerminateYarn(AYarn: TIdYarn); var LThread: TIdThreadWithTask; begin if not Assigned(AYarn) then Exit; LThread := TIdYarnOfThread(AYarn).Thread; if Assigned(LThread) then begin // 发送停止信号 LThread.Stop; // 最多等待3秒让线程正常退出 if LThread.WaitFor(3000) = wrTimeout then begin // 超时强制终止线程 LThread.Terminate; LThread.WaitFor; end; end; // 主动释放Yarn对象,会自动从活跃列表中移除 FreeAndNil(AYarn); end;
修改后重新编译Indy库即可,该修改对TIdTCPServer、TIdHTTPServer等所有基于该调度器的组件都生效,不会影响原有功能。
方案2:替换为线程池调度器规避问题
如果不想修改源码,也可以在初始化TIdHTTPServer时,手动指定使用线程池调度器TIdSchedulerOfThreadPool,该调度器的Yarn回收逻辑已处理了线程退出的等待逻辑,不会出现Yarn残留问题,示例代码如下:
uses ..., IdSchedulerOfThreadPool; procedure TYourServiceForm.ServiceInit; begin // 创建线程池调度器绑定到HTTPServer IdHTTPServer1.Scheduler := TIdSchedulerOfThreadPool.Create(Self); // 按需配置线程池大小 TIdSchedulerOfThreadPool(IdHTTPServer1.Scheduler).PoolSize := 20; // 启动服务 IdHTTPServer1.Active := True; end;
内容的提问来源于stack exchange,提问作者Bart Kindt
相关产品推荐
相关产品推荐

