Delphi 在工作线程中断开 TIdTCPClient

Delphi disconnecting TIdTCPClient in worker thread

我需要通过 FMX 应用程序向设备发送 TCP 字节数组。我有这个界面:

type
  IPacketSend = interface
    procedure SendAsync(const Msg: String; OnSuccess: TSendSuccess; OnError: TSendError);
  end;

我必须使用线程来不阻塞 UI。 class 实际上以非常简化的版本发送消息:

type
  TPacketSenderLAN = class(TInterfacedObject, IPacketSend)
    private      
      FSelf: IPacketSend;
    public      
      procedure SendAsync(const Msg: String; OnSuccess: TSendSuccess; OnError: TSendError);
  end;

implementation

{ TPacketSender<T> }

procedure TPacketSenderLAN.SendAsync(const Msg: String; OnSuccess: TSendSuccess;
  OnError: TSendError);
begin
  TTask.Run(
    procedure
    var
      Client: TIdTCPClient;
      Exc: TObject;
    begin
      Client := TIdTCPClient.Create(nil);
      try
        try
          Client.Host := '192.168.0.213';
          Client.Port := 5200;
          Client.ConnectTimeout := 3500;

          Client.Connect;

          Data := TIdBytes(...);
          Client.Socket.Write(Data);

          TThread.Synchronize(nil,
            procedure
            begin
              OnSuccess;
              FSelf := nil;
            end
          );
        except
          on E: Exception do
            begin
              Exc := AcquireExceptionObject;

              TThread.Synchronize(nil,
                procedure
                begin
                  OnError(Exception(exc).Message);
                  FSelf := nil;
                end
              );
            end;
        end;
      finally
        Client.Free;
      end;
    end
  );
end;

end.

FSelf 变量是绝对需要的,因为在构造函数中使用 FSelf := Self; 我可以防止工作线程执行时引用计数变为 0。其实我叫...

TThread.Synchronize(nil,
  procedure
    begin
      OnSuccess;
      FSelf := nil;
     end
);

... 其中 FSelf := nil; 位于末尾,以便在作业完成后处理该对象。我从代码中这样调用它:

var
  PacketSender: IPacketSend;
begin
  PacketSender := TPacketSenderLAN.Create(...);
end;

鉴于上述情况,我的问题是:

am I using the TIdTCPClient safely? Do I have to disconnect it?

我不知道是否应该在 finally 块中调用 Client.Disconnect;。我认为不需要,因为 Free 会破坏 TIdTCPClient,因此客户端会断开连接。我的代码安全吗?

am I using the TIdTCPClient safely?

是的,你是。

Data,另一方面,不是那么多,因为它没有显示为局部变量,甚至不是 TPacketSenderLAN class 的成员,这意味着它必须是一个全局变量,因此会受到多线程并发问题的影响。在这种情况下,它应该是一个局部变量。

Do I have to disconnect it?

我会推荐它,是的,特别是在调用您的 OnSuccess/OnError 处理程序之前。如果不手动调用Disconnect(),则TCP连接不会断开,直到调用TIdTCPClient析构函数。在此代码中,当您的事件处理程序 运行 时,TCP 连接没有理由保持活动状态。

I don't know if I should call Client.Disconnect; inside the finally block.

我实际上建议添加 另一个 try..finally 块只是为了调用 Disconnect(),例如:

procedure
var
  Client: TIdTCPClient;
  Data: TIdBytes;
begin
  try
    Client := TIdTCPClient.Create(nil);
    try
      Client.Host := '192.168.0.213';
      Client.Port := 5200;
      Client.ConnectTimeout := 3500;

      Client.Connect;
      try      
        Data := TIdBytes(...);
        Client.IOHandler.Write(Data);
      finally
        Client.Disconnect;
      end;
    finally
      Client.Free;
    end;
  except
    on E: Exception do
    begin
      TThread.Synchronize(nil,
        procedure
        begin
          OnError(E.Message);
        end
      );
      Exit;
    end;
  end;

  TThread.Synchronize(nil,
    procedure
    begin
      OnSuccess;
    end
  );
end