3

dll を介して外部ハードウェアと通信するソフトウェアを作成しています (いくつかのモーターを移動し、いくつかの値を読み戻します)。dll への呼び出しがブロックされており、10 秒ほど返されない場合があります。ソフトウェアは、ハードウェアを動かしてスキャンを実行し、読み取りを行い、いくつかのポイントを繰り返します。1 回のスキャンが完了するまでに 30 分程度かかる場合があります。スキャンが実行されている間、GUI が応答し、受信データのライブ グラフ (MDI チャイルド内) が各ポイントで更新されることは明らかです。マルチスレッドは、この問題に対する明白な選択のようです。

私の質問は、これをスレッド化し、メインの VCL スレッドに戻ってスキャン中にグラフを更新する最良の方法は何ですか?

私は現在、「スキャンロジック」を実行する単一の TThread 子孫と、ChildForm の public var セクションに double の配列を持っています。スレッドからこの配列に入力する必要がありますが、Synchronize または CriticalSection または PostMessage またはその他のメソッドを使用するかどうかはわかりません。新しい値が追加されるたびに、メイン VCL スレッドはグラフを更新する必要があります。グローバル変数であるデータの中間オブジェクトが本当に必要であり、Thread と ChildForm から別々に何らかの方法でこれにアクセスする必要がありますか?

4

4 に答える 4

6

スレッドから GUI を更新する最も簡単な方法は、および とanonymous methods組み合わせて使用​​することです。TThread.SynchronizeTThread.Queue

procedure TMyThread.Execute;
begin
  ...
  Synchronize(  // Synchronous example
    procedure
    begin
      // Your code executed in main thread here 
    end
  );
  ...
  Queue( // Asynchronous example
    procedure
    begin
      // Your code executed in main thread here
    end
  );
end;

非同期で値を渡すには、多くの場合、値を「キャプチャ」する必要があります。

procedure TMyThread.PassAValue(anInteger: Integer);
begin
  Queue(
    procedure
    begin
      // Use anInteger in main thread 
    end
  );
end;

procedure TMyThread.Execute;
var
  myInt: Integer;
begin
  ...
  PassAValue(myInt);  // Capture myInt
  ...
end;

匿名メソッドが変数を使用している場合、変数への参照がキャプチャされます。これは、匿名メソッドが実行される前に変数の値を変更すると、代わりに新しい値が使用されることを意味します。したがって、「価値」を捉える必要があります。

synchronize-and-queue-with-parametersより精巧な例は、ここで見つけることができます@UweRaabe

于 2013-10-01T12:05:41.687 に答える
1

バックグラウンド スレッドからデータを入力し、メイン スレッドにリストに新しい項目があるというメッセージを投稿してTThreadListから、メイン スレッドでリストを処理するのは簡単で、保守も簡単です。

この方法を使用すると、必要な数の読み取り値をリストに格納でき、メイン スレッドがメッセージを受信するたびに、リスト内のすべての項目を一度に処理するだけです。

読み取り値のクラスを定義してインスタンス化し、バックグラウンド スレッドのリストに追加します。それらをリストから外すときは、メイン スレッドでそれらを解放することを忘れないでください。

于 2013-10-01T13:50:44.980 に答える
1

メインスレッドをブロックする単純な Synchronize 呼び出しよりも少し投資したい場合は、その上にメッセージングを含む単純な FIFO キューを追加できます。データの流れは次のようになります。

  1. スレッドはデータをキューに入れます。
  2. スレッドは、メイン スレッド ウィンドウにメッセージを投稿します。私はどちらが気にしません:)
  3. データが利用可能であるというメッセージを処理し、キュー内のメッセージを必要に応じて処理します。

コードは次のようになります。

待ち行列...

const
  WM_DataAvailable = WM_USER + 1;

var
  ThreadSafeQueue: TThreadSafeQueue;

データはキューに入れられます...

procedure PutDataIntoQueue;
var
  MyObject: TMyObject;
begin
  MyObject := TMyObject.Create;
  ThreadSafeQueue.Enqueue(MyObject);
  PostMessage(FMainWindowHandle, WM_DataAvailable, 0, 0);
end;

と処理...

procedure ProcessDataInTheQueue(var Msg: TMessage); message WM_DataAvailable;

procedure ProcessDataInTheQueue(var Msg: TMessage);
var
  AnyValue: TAnyValue;
  MyObject: TMyObject;
begin
  while ThreadSafeQueue.Dequeue(AnyValue) do
  begin
    MyObject := TMyObject(AnyValue.AsObject);
    try
      // process the actual object as needed
    finally
      MyObject.Free
    end;
  end;
end;

コードは Delphi およびチェックなしで記述されているため、エラーが含まれる可能性があります。無料で入手できるスレッド セーフ キューと TAnyValue を使用した例を示しました。ここで両方を見つけることができます:

http://www.cromis.net/blog/downloads/

また、PostMessage が実際に送信されたかどうかを確認していないことに注意してください。本番コードで確認する必要があります。

于 2013-10-01T13:19:53.957 に答える
0

スレッド内で postmessage を使用し、メッセージをメイン フォーム ハンドルに送信します。1 つ (または複数) のカスタム メッセージを登録し、それらのハンドラーを記述します。

const WM_MEASURE_MESSAGE = WM_USER + 1;

スレッド クラスを作成し、MainFormHandle プロパティ (Thandle または cardinal) を追加します。中断されたスレッドを作成し、MainFormHandle にメイン フォーム ハンドルを設定してから、スレッドを再開します。新しいメジャーがある場合は、data1 と data2 dword にメジャーからのデータを割り当ててから、

PostMessage(fMainFormHandle,WM_MEASURE_MESSAGE,data1,data2);

メイン フォームには、メッセージ ハンドラがあります。

procedure MeasureMessage(var msg: TMessage); message WM_MEASURE_MESSAGE;
begin
  // update graph here
  // msg.wparam is data1
  // msg.lparam is data2
end;

スレッドからメイン フォームにさらに多くのデータを送信する必要がある場合は、測定データ全体に対してメイン コンテキストで適切な構造を作成し、スレッドへの参照を渡し、スレッドにデータを書き込み、メッセージを使用してメイン フォームに新しいデータを伝えることができます。位置 (配列インデックスなど)。メイン コンテキストで TThread.Waitfor を使用して、スレッドがまだ実行中 (およびメモリに書き込み中) にデータ構造を解放しないようにします。

于 2013-10-01T11:05:15.210 に答える