Delphi отключение TIdTCPClient в рабочем потоке - PullRequest
1 голос
/ 24 апреля 2020

Мне нужно отправить массив байтов TCP через приложение FMX на устройство. У меня есть этот интерфейс:

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

Я должен использовать многопоточность, чтобы не блокировать пользовательский интерфейс. Этот класс на самом деле отправляет сообщение в очень упрощенной версии:

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; в конструкторе я предотвращаю подсчет ссылок до go до 0, когда рабочий поток выполняется. На самом деле я звоню ...

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

... где FSelf := nil; находится в конце, так что объект удаляется по окончании работы. Я называю это таким образом из кода:

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

Учитывая приведенный выше сценарий, мой вопрос:

Безопасно ли я использую TIdTCPClient? Должен ли я отключить его?

Я не знаю, стоит ли мне звонить Client.Disconnect; внутри блока finally. Я думаю, что это не нужно, потому что Free уничтожит TIdTCPClient и, таким образом, клиент отключится. Мой код безопасен?

1 Ответ

3 голосов
/ 24 апреля 2020

Безопасно ли я использую TIdTCPClient?

Да, вы.

Data, с другой стороны, не так много, так как это не так показана как локальная переменная или даже член класса TPacketSenderLAN, что означает, что она должна быть глобальной переменной, и, следовательно, будет подвержена многопоточным проблемам параллелизма. В этом случае это должна быть локальная переменная.

Нужно ли ее отключать?

Я бы порекомендовал, да, особенно перед вызовом вашего OnSuccess / OnError обработчики. Если вы не вызовете Disconnect() вручную, TCP-соединение не будет отключено, пока не будет вызван деструктор TIdTCPClient. В этом коде нет никаких причин для того, чтобы соединение TCP оставалось активным, пока работают ваши обработчики событий.

Я не знаю, стоит ли мне звонить Client.Disconnect; внутри блока finally.

Я бы действительно предложил добавить еще 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
...