5

質問:私が探しているのは、Indy 10 で IdTCPClient を使用して別のスレッドを使用してデータを受信する最も一般的またはベスト プラクティスの方法です。

背景:以下のコードは、わかりやすくするために実際のデータ処理部分を取り除いたサンプルです。スレッドのアイデアは、すべてのデータ (残りのメッセージ長を宣言するヘッダーを持つ可変サイズ) を受信し、それを解析して (HandleData プロシージャが行うことです)、コマンドに応じてイベント ハンドラーをトリガーすることです。

TIdIOHandlerSocket は、必要に応じてソケットにデータを書き込むメイン アプリケーションによってスレッドに渡されます。

TScktReceiveThread = class(TThread)
  private
    { Private declarations }
    procedure HandleData;
  protected
    procedure Execute; override;
  public
    FSocket: TIdIOHandlerSocket;
    constructor Create(CreateSuspended: boolean);
  end;


procedure TScktReceiveThread.Execute;
var
  FixedHeader: TBytes;
begin
  Assert(FSocket <> nil, 'You must assign the connected socket to the receiving thread');
  SetLength(FixedHeader, 2);
   while not Terminated do
    begin
      if not FSocket.Connected then
        Suspend
      else
        begin
          FSocket.CheckForDataOnSource(10);
          if not FSocket.InputBufferIsEmpty then
           begin
            FSocket.ReadBytes(FixedHeader, SizeOf(FixedHeader), false);
            // Removed the rest of the reading and parsing code for clarity
            Synchronize(HandleData);
           end;
        end;
    end;
end;

プレフィックスとして、Indy のサーバー コンポーネントを扱う別の StackOverflow の質問を使用しました。「Delphi 2009、Indy 10、TIdTCPServer.OnExecute、InputBuffer 内のすべてのバイトを取得する方法」を使用して、これまでの内容の基礎を取得しました。 .

助けてくれてありがとう!

4

2 に答える 2

8

クライアント/サーバーデータ交換ごとにスレッドクラスを作成することによって課せられるオーバーヘッドを回避したい場合は、次のように動作可能なスレッドクラスを作成できます。

http://delphidicas.blogspot.com/2008/08/anonymous-methods-when-should-they-be.html

数日前に同じ問題が発生し、D2009の新しい匿名メソッド機能を使用してスレッドを作成できる静的関数を持つクラスTMotileThreadingを作成しました。次のようになります。

type
  TExecuteFunc = reference to procedure;

  TMotileThreading = class
  public
    class procedure Execute (Func : TExecuteFunc);
    class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc);
  end;

2番目の手順では、あなたの場合のようにクライアント/サーバー通信を実行し、データが到着するたびにいくつかの処理を実行できます。匿名メソッドの良いところは、呼び出し元のコンテキストのローカル変数を使用できることです。したがって、通信は次のようになります。

var
  NewData  : String;
begin
  TMotileThreading.ExecuteThenCall (
    procedure
    begin
      NewData := IdTCPClient.IOHandler.Readln;
    end,
    procedure
    begin
      GUIUpdate (NewData);
    end);
 end;

ExecuteメソッドとExecuteThenCallメソッドは、ワーカースレッドを作成し、FreeOnTerminateをtrueに設定するだけで、メモリ管理を簡素化し、ワーカースレッドのExecuteおよびOnTerminateプロシージャで提供される関数を実行します。

お役に立てば幸いです。

編集(クラスTMotileThreadingの完全な実装の要求に応じて)

type
  TExecuteFunc = reference to procedure;

  TMotileThreading = class
  protected
    constructor Create;
  public
    class procedure Execute (Func : TExecuteFunc);
    class procedure ExecuteAndCall (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
                                SyncTerminateFunc : Boolean = False);
  end;

  TMotile = class (TThread)
  private
    ExecFunc             : TExecuteFunc;
    TerminateHandler     : TExecuteFunc;
    SyncTerminateHandler : Boolean;
  public
    constructor Create (Func : TExecuteFunc); overload;
    constructor Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
                        SyncTerminateFunc : Boolean); overload;
    procedure OnTerminateHandler (Sender : TObject);
    procedure Execute; override;
  end;

implementation

constructor TMotileThreading.Create;
begin
  Assert (False, 'Class TMotileThreading shouldn''t be used as an instance');
end;

class procedure TMotileThreading.Execute (Func : TExecuteFunc);
begin
  TMotile.Create (Func);
end;

class procedure TMotileThreading.ExecuteAndCall (Func : TExecuteFunc;
                                                 OnTerminateFunc : TExecuteFunc;
                                                 SyncTerminateFunc : Boolean = False);
begin
  TMotile.Create (Func, OnTerminateFunc, SyncTerminateFunc);
end;

constructor TMotile.Create (Func : TExecuteFunc);
begin
  inherited Create (True);
  ExecFunc := Func;
  TerminateHandler := nil;
  FreeOnTerminate := True;
  Resume;
end;

constructor TMotile.Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
                            SyncTerminateFunc : Boolean);
begin
  inherited Create (True);
  ExecFunc := Func;
  TerminateHandler := OnTerminateFunc;
  SyncTerminateHandler := SyncTerminateFunc;
  OnTerminate := OnTerminateHandler;
  FreeOnTerminate := True;
  Resume;
end;

procedure TMotile.Execute;
begin
  ExecFunc;
end;

procedure TMotile.OnTerminateHandler (Sender : TObject);
begin
  if Assigned (TerminateHandler) then
    if SyncTerminateHandler then
      Synchronize (procedure
                   begin
                     TerminateHandler;
                   end)
    else
      TerminateHandler;
end;
于 2009-02-17T07:40:53.217 に答える
6

あなたは正しい軌道に乗っています。Indy はそのように使用されることを意図しています。ブロッキングソケットを使用するため、ReadBytes要求したものが読み取られるまで呼び出しは返されません。呼び出しが早期に返される可能性があるノンブロッキング ソケットとは対照的です。そのため、ポーリングするか、非同期で通知を受けて、要求がいつ満たされたかを判断します。

Indy は、ソケット オブジェクトが独自のスレッド (またはファイバー) を持つことを想定して設計されています。Indy は、TIdAntifreezeソケット コンポーネントをフォームやデータ モジュールにドラッグ アンド ドロップし、メイン GUI スレッドから Indy コンポーネントを使用したい人向けに付属していますが、それを避けることができれば、一般的には良い考えではありません。

スレッドは割り当てられないと機能しないFSocketため、クラスのコンストラクターでその値を単純に受け取ることをお勧めします。割り当てられていない場合は、コンストラクターでアサートします。さらに、中断されていないスレッドを作成するのはエラーなので、なぜオプションを与えるのですか? (スレッドが一時停止されていない場合は、実行が開始され、割り当てられているかどうかFSocketがチェックされ、作成中のスレッドがまだそのフィールドを割り当てられていないため、失敗します。)

于 2009-02-16T19:15:34.710 に答える