在表单关闭时停止线程并断开 Indy tcp 客户端
Stopping thread and disconnecting indy tcp client on form close
我确实写了一个应用程序,它使用 Indy 10 TCP/IP 客户端和 TThread
。该应用程序在 Form.OnCreate
事件时连接到服务器,并在 Form.OnClose
事件时断开连接。在TThread
.
中实现与服务器的连接
当我在以太网电缆断开连接的情况下启动应用程序并尝试关闭应用程序直到连接超时时,我确实遇到了这两个异常:
- Socket.Error #10038 非套接字上的套接字操作。
- 线程错误:句柄无效(6)。
如果我在连接到客户端时尝试关闭应用程序,那么我只会得到这个异常:
- 线程错误:句柄无效(6)。
如果我在线程执行睡眠时关闭应用程序,那么我不会得到异常。
我做错了什么,或者这是正常行为?
TThread
class代码:
type
connThread = class (TThread)
protected
procedure Execute ; override;
private
procedure Sinchronizuot(zinute : string; spalva : TColor; tmrNormalReconn : Boolean);
end;
Form.OnCreate
代码:
procedure TForm1.FormCreate(Sender: TObject);
begin
fellnerConn := connThread.Create(True);
fellnerConn.FreeOnTerminate := True;
fellnerConn.Start;
end;
Form.OnClose
代码:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if fellnerConn <> nil then
fellnerConn.Terminate;
if idCl.Connected then
begin
try
idCl.Disconnect;
idCl.IOHandler.Free;
finally
if fellnerConn <> nil then
begin
fellnerConn.WaitFor;
fellnerConn := nil;
end;
end;
end;
end;
线程执行代码:
procedure connThread.Execute;
var
zinute : string;
spalva : TColor;
begin
inherited;
while not Form1.fellnerConn.Terminated do
begin
zinute := 'Jungiamasi prie Moxa serverio ' + Form1.idCl.Host;
spalva := clYellow;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
try
Form1.idCl.Connect;
except
on E: Exception do
begin
zinute := e.Message + ' Nepavyko prisijungti.';
spalva := clWebRed;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end);
Sleep(1000);
end;
end;
end;
end;
套接字错误在意料之中。主线程正在关闭套接字,而工作线程仍在使用它。
但是,您不能将 TThread.WaitFor()
与 FreeOnTerminate=True
一起使用,这就是您不断收到 "the handle is invalid" 错误的原因。正在销毁线程对象,关闭其句柄,而 WaitFor
仍在使用它。
你不应该这样使用 FreeOnTerminate
。它应该只用于启动后忘记类型的线程。一旦需要保留对线程对象的引用,就不应再使用它的 FreeOnTerminate
属性。
无论哪种方式,您都应该使用线程的 OnTerminate
事件,这样您就可以 nil
在线程终止后立即引用该线程。
试试像这样的东西:
type
connThread = class (TThread)
protected
FClient: TIdTCPClient;
procedure Execute; override;
private
procedure Sinchronizuot(zinute : string; spalva : TColor; tmrNormalReconn : Boolean);
public
constructor Create(Client: TIdTCPClient); reintroduce;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fellnerConn := connThread.Create(IdCl);
fellnerConn.OnTerminate := ThreadTerminated;
fellnerConn.Start;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if fellnerConn <> nil then
fellnerConn.Terminate;
try
idCl.Disconnect;
finally
if fellnerConn <> nil then
begin
fellnerConn.OnTerminate := nil;
fellnerConn.WaitFor;
FreeAndNil(fellnerConn);
end;
end;
end;
procedure TForm1.ThreadTerminated(Sender: TObject);
begin
fellnerConn := nil;
TThread.ForceQueue(nil, Sender.Free);
end;
constructor connThread.Create(Client: TIdTCPClient);
begin
inherited Create(True);
FClient := Client;
end;
procedure connThread.Execute;
var
zinute : string;
spalva : TColor;
begin
while not Terminated do
begin
zinute := 'Jungiamasi prie Moxa serverio ' + FClient.Host;
spalva := clYellow;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
try
FClient.Connect;
except
on E: Exception do
begin
zinute := e.Message + ' Nepavyko prisijungti.';
spalva := clWebRed;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
if Terminated then Exit;
Sleep(1000);
Continue;
end;
end;
try
// use FClient as needed...
finally
FClient.Disconnect;
end;
end;
end;
我确实写了一个应用程序,它使用 Indy 10 TCP/IP 客户端和 TThread
。该应用程序在 Form.OnCreate
事件时连接到服务器,并在 Form.OnClose
事件时断开连接。在TThread
.
当我在以太网电缆断开连接的情况下启动应用程序并尝试关闭应用程序直到连接超时时,我确实遇到了这两个异常:
- Socket.Error #10038 非套接字上的套接字操作。
- 线程错误:句柄无效(6)。
如果我在连接到客户端时尝试关闭应用程序,那么我只会得到这个异常:
- 线程错误:句柄无效(6)。
如果我在线程执行睡眠时关闭应用程序,那么我不会得到异常。
我做错了什么,或者这是正常行为?
TThread
class代码:
type
connThread = class (TThread)
protected
procedure Execute ; override;
private
procedure Sinchronizuot(zinute : string; spalva : TColor; tmrNormalReconn : Boolean);
end;
Form.OnCreate
代码:
procedure TForm1.FormCreate(Sender: TObject);
begin
fellnerConn := connThread.Create(True);
fellnerConn.FreeOnTerminate := True;
fellnerConn.Start;
end;
Form.OnClose
代码:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if fellnerConn <> nil then
fellnerConn.Terminate;
if idCl.Connected then
begin
try
idCl.Disconnect;
idCl.IOHandler.Free;
finally
if fellnerConn <> nil then
begin
fellnerConn.WaitFor;
fellnerConn := nil;
end;
end;
end;
end;
线程执行代码:
procedure connThread.Execute;
var
zinute : string;
spalva : TColor;
begin
inherited;
while not Form1.fellnerConn.Terminated do
begin
zinute := 'Jungiamasi prie Moxa serverio ' + Form1.idCl.Host;
spalva := clYellow;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
try
Form1.idCl.Connect;
except
on E: Exception do
begin
zinute := e.Message + ' Nepavyko prisijungti.';
spalva := clWebRed;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end);
Sleep(1000);
end;
end;
end;
end;
套接字错误在意料之中。主线程正在关闭套接字,而工作线程仍在使用它。
但是,您不能将 TThread.WaitFor()
与 FreeOnTerminate=True
一起使用,这就是您不断收到 "the handle is invalid" 错误的原因。正在销毁线程对象,关闭其句柄,而 WaitFor
仍在使用它。
你不应该这样使用 FreeOnTerminate
。它应该只用于启动后忘记类型的线程。一旦需要保留对线程对象的引用,就不应再使用它的 FreeOnTerminate
属性。
无论哪种方式,您都应该使用线程的 OnTerminate
事件,这样您就可以 nil
在线程终止后立即引用该线程。
试试像这样的东西:
type
connThread = class (TThread)
protected
FClient: TIdTCPClient;
procedure Execute; override;
private
procedure Sinchronizuot(zinute : string; spalva : TColor; tmrNormalReconn : Boolean);
public
constructor Create(Client: TIdTCPClient); reintroduce;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fellnerConn := connThread.Create(IdCl);
fellnerConn.OnTerminate := ThreadTerminated;
fellnerConn.Start;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if fellnerConn <> nil then
fellnerConn.Terminate;
try
idCl.Disconnect;
finally
if fellnerConn <> nil then
begin
fellnerConn.OnTerminate := nil;
fellnerConn.WaitFor;
FreeAndNil(fellnerConn);
end;
end;
end;
procedure TForm1.ThreadTerminated(Sender: TObject);
begin
fellnerConn := nil;
TThread.ForceQueue(nil, Sender.Free);
end;
constructor connThread.Create(Client: TIdTCPClient);
begin
inherited Create(True);
FClient := Client;
end;
procedure connThread.Execute;
var
zinute : string;
spalva : TColor;
begin
while not Terminated do
begin
zinute := 'Jungiamasi prie Moxa serverio ' + FClient.Host;
spalva := clYellow;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
try
FClient.Connect;
except
on E: Exception do
begin
zinute := e.Message + ' Nepavyko prisijungti.';
spalva := clWebRed;
Synchronize(procedure
begin
Sinchronizuot(zinute, spalva, False);
end
);
if Terminated then Exit;
Sleep(1000);
Continue;
end;
end;
try
// use FClient as needed...
finally
FClient.Disconnect;
end;
end;
end;