【发布时间】: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()。