当前位置:  开发笔记 > 运维 > 正文

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

如何解决《释放和终止使用TIdTCPClient组件的线程的正确方法是什么?》经验,为你挑选了1个好方法。

我正在使用Delphi 10 Seattle来使用TIdTCPClientTIdTCPServer组件构建一个简单的客户端/服务器应用程序.

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

这是Execute方法

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;

在正常情况下一切正常,但现在我正在做一些测试,以恢复客户端应用程序上的连接,以防服务器或网络出现故障.所以我关闭服务器App以模拟通信失败时的问题.

发生这种情况时,客户端应用程序会使用该TIdTCPClient.OnStatus事件检测服务器已消失.

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

  if Assigned(FClientReadThr) then
  begin
    FClientReadThr.Terminate;
    FClientReadThr.WaitFor; // This never returns.
    FreeAndNil(FClientReadThr);
  end;

WaitFor功能永远不会回归.

所以问题是,我的执行程序有什么问题阻止了线程的最终化?

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



1> Remy Lebeau..:

首先,你不应该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()线程.

推荐阅读
135369一生真爱_890
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有