Delphi 释放和终止使用TIdTCPClient组件的线程的正确方法是什么?

Delphi 释放和终止使用TIdTCPClient组件的线程的正确方法是什么?,delphi,indy,Delphi,Indy,我正在使用Delphi10西雅图构建一个简单的客户机/服务器应用程序,使用TIdTCPClient和TIdTCPServer组件 为了读取从服务器应用程序(TIdTCPServer)到达的数据,我在客户端应用程序中使用了一个线程 这是执行方法 procedure TClientReadThread.Execute; begin while not Terminated do begin try if FClient.Connected() then //F

我正在使用Delphi10西雅图构建一个简单的客户机/服务器应用程序,使用
TIdTCPClient
TIdTCPServer
组件

为了读取从服务器应用程序(TIdTCPServer)到达的数据,我在客户端应用程序中使用了一个线程

这是执行方法

procedure TClientReadThread.Execute;
begin
  while not Terminated do
  begin
    try      
      if FClient.Connected() then //FClient is TIdTCPClient
      begin
       if not FClient.IOHandler.InputBufferIsEmpty then
       begin
         AResponse := FClient.IOHandler.ReadLn();
         Synchronize(NotifyReadln);
       end
       else
       FClient.IOHandler.CheckForDataOnSource(10);
      end;

    except
      on E: Exception do
      begin
        // Send the exception message to the logger
        FE:=E;
        Synchronize(LogException);
      end;
    end;
  end;
end;
在正常情况下,一切正常,但现在我正在做一些测试,以恢复客户端应用程序上的连接,以防服务器或网络出现故障。因此,我关闭了服务器应用程序,以模拟通信失败时出现的问题

当这种情况发生时,客户机应用程序使用
TIdTCPClient.OnStatus
事件检测哪个服务器不在了

之后,我尝试使用以下代码终止读取线程

  if Assigned(FClientReadThr) then
  begin
    FClientReadThr.Terminate;
    FClientReadThr.WaitFor; // This never returns.
    FreeAndNil(FClientReadThr);
  end;
但是
WaitFor
函数永远不会返回

所以问题是,在我的执行过程中有什么错误阻止了线程的终结


是否有更好的终止线程的方法?

首先,您不应该以这种方式使用
Connected()
。只要无条件地调用
ReadLn()
,并让它在发生错误/断开连接时引发异常:

procedure TClientReadThread.Execute;
begin
  while not Terminated do
  begin
    try      
     AResponse := FClient.IOHandler.ReadLn();
     Synchronize(NotifyReadln);
    except
      // ...
    end;
  end;
end;
如果要手动轮询套接字以获取数据,则应如下所示:

procedure TClientReadThread.Execute;
begin
  while not Terminated do
  begin
    try      
     if FClient.IOHandler.InputBufferIsEmpty then
     begin
       FClient.IOHandler.CheckForDataOnSource(10);
       FClient.IOHandler.CheckForDisconnect;
       if FClient.IOHandler.InputBufferIsEmpty then Continue;
     end;
     AResponse := FClient.IOHandler.ReadLn();
     Synchronize(NotifyReadln);
    except
      // ...
    end;
  end;
end;
在这种情况下,不要使用
TIdTCPClient.OnStatus
事件来检测断开连接。如果直接在
OnStatus
事件处理程序中终止线程,则是死锁代码。该事件将在线程上下文中调用,因为该线程是读取连接并检测断开连接的线程。因此,线程最终会自行等待,这就是
WaitFor()
不会退出的原因

我建议另一种方法。根本不要终止线程。要恢复连接,请向线程添加另一级别的循环,并让它自动检测断开和重新连接:

procedure TClientReadThread.Execute;
var
  I: Integer;
begin
  while not Terminated do
  begin
    try      
      // don't call Connect() in the main thread anymore, do it here instead
      FClient.Connect;
    except
      // Send the exception message to the logger

      // you should wait a few seconds before attempting to reconnect,
      // don't flood the network with connection requests...
      for I := 1 to 5 do
      begin
        if Terminated then Exit;
        Sleep(1000);
      end;

      Continue;
    end;

    try
      try
        while not Terminated do
        begin
          AResponse := FClient.IOHandler.ReadLn();
          Synchronize(NotifyReadln);
        end;
      except
        // Send the exception message to the logger
      end;
    finally
      FClient.Disconnect;
    end;
  end;
end;
然后,当您想停止使用套接字I/O时,可以正常地
Terminate()
WaitFor()
线程