Остановка потока и отключение indy tcp клиента при закрытии формы - PullRequest
0 голосов
/ 07 ноября 2018

Я пишу приложение, которое использует Indy 10 TCP / IP Client и TThread. Приложение подключается к серверу по событию Form.OnCreate и отключается от него по событию Form.OnClose. Подключение к серверу осуществляется в TThread.

Когда я запускаю приложение, когда кабель Ethernet отключен, и пытаюсь закрыть приложение до истечения времени подключения, я получаю два исключения:

  • Socket.Error # 10038 Операция с сокетом без сокета.
  • Ошибка потока: недопустимый дескриптор (6).

Если я пытаюсь закрыть приложение, пока оно подключено к клиенту, я получаю только это исключение:

  • Ошибка потока: недопустимый дескриптор (6).

Если я закрою приложение, пока поток выполняет сон, то никаких исключений я не получу.

Что я делаю не так, или это нормальное поведение?

TThread код класса:

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;

1 Ответ

0 голосов
/ 07 ноября 2018

Ожидается ошибка сокета. Основной поток закрывает сокет, пока рабочий поток все еще использует его.

Но вы не можете использовать TThread.WaitFor() с FreeOnTerminate=True, поэтому вы продолжаете получать сообщения «недопустимый дескриптор». Объект потока уничтожается, закрывая свой дескриптор, пока 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;
...