【问题标题】:Delphi disconnecting TIdTCPClient in worker threadDelphi在工作线程中断开TIdTCPClient
【发布时间】:2020-08-08 05:11:11
【问题描述】:

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

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

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

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;

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

我是否安全地使用了 TIdTCPClient?我必须断开它吗?

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

【问题讨论】:

  • TThread 在内部增加自己的引用计数,因此您不需要手动保持线程处于活动状态。 TTask 使用一个匿名线程,它会在您的过程退出后释放自己。我在这段代码中看不到任何需要FSelf 的东西。另外,我认为没有理由使用AcquireExceptionObject(),因为您使用TThread.Synchronize() 可以直接捕获E,但最好将E.Message 保存到局部变量并让TThread.Synchronize() 捕获它。此外,在调用OnSuccess/OnError 之前,您不会调用Client.Disconnect()。不过,我建议使用TThread.Queue()

标签: delphi indy


【解决方案1】:

我是否安全地使用了 TIdTCPClient?

是的,你是。

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

我必须断开它吗?

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

我不知道我是否应该在finally 块内调用Client.Disconnect;

我实际上建议添加 另一个 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

【讨论】:

    猜你喜欢
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    相关资源
    最近更新 更多