質問

スレッドと同期に関する別の質問に戻ってきました。長い操作を実行する必要があるサーバーアプリケーションを想像してください。クライアントは、サーバーの応答を待っている間、GUIが応答性を維持し続けることを望んでいます。私は次のパターンを考えました:

TMonitor.Enter (FTCPClient);
try
  WorkerThread := TWorkerThread.Create (SomeLengthyServerOperation);
  while (not WorkerThread.Ready) do
    Application.ProcessMessages;
  DoSometingWithResults (WorkerThread.Result);
  WorkerThread.Free;      
finally
  TMonitor.Exit (FTCPClient);
end;

WorkerThreadは、コンストラクターに渡されてから終了する関数を実行するTThReadから派生した単純なクラスです(Ready = Trueで結果を結果として)。提示されたコードは、ボタンがクリックされるたびに実行されます。

私の質問に:ボタンを2回非常に速くクリックすると、サーバーとクライアントの間でコミュニケーションが非常に似ているように見える奇妙なエラーがいくつかあります。 Application.ProcessMessagesが実行された後のイベントハンドラーはどのスレッドですか?スレッドあたりのtmonitorのロックはありますか?これは、application.processmessagesを使用した場合、ロックが機能しないことを意味しますか?

現時点では説明することはできません。誰かが私の主張を得ることを願っています。そうでない場合は、お気軽に質問してください。

編集:ボタンの無効化と有効化について:クライアントコードについて何も知りません。ボタンイベントハンドラーである可能性があり、他のものかもしれません。基本的に、クライアントコードからロックを隠したいと思います。

役に立ちましたか?

解決

tmonitorはブロックするだけです 違う ロックの取得からスレッド。これはこれです。ロック内からのメッセージを処理することにより、同じスレッドで同じ関数に戻り、ロックの再帰的な獲得を引き起こしています。次に、コードが新しいワーカースレッドを作成し、サイクルを開始します。ワーカースレッドが完了するまで再度クリックできないように、ボタンを無効にすることができます。必ずボタンを無効にしてください メッセージの処理を開始し、別のトライを使用して、均一にブロックして、再度有効になっていることを確認します。コードの残りの部分によってどのように配置されているかによって、ロックさえ必要ない場合があります。

他のヒント

いくつかのコメント:

  1. あなたのworkerthreadは、あなたがちょうど再実装されたように聞こえます asynccalls. 。試行されてテストされた実装を使用することは、おそらく自分のものを書くよりも優れています(学習効果のためにそれを行う場合、または非常に特別な要件があるため)。両方を見てください asynccalls そしてその OmnithReadLibrary.

  2. ロックは可能な限り短いだけである必要があるため、反応全体をモニター()と監視()とexit()のボタンクリックに包むことは間違っているようです。また、これがどのような目的に役立つかはあまり理解できません。

  3. Application.ProcessMessages()を呼び出している間、ロックが保持されている間、あらゆる種類の厄介な驚きをもたらすことができます。コードが再び入らないようにする必要がある場合は、一般に、オンクリックハンドラーが最初に行うようにすべてのUI要素を無効にし、ハンドラーが終了したときにそれらを再エネングできるようにすることが最善です。また、ロックは同じスレッドから複数回入力できることに注意してください。 多数 スレッド。

  4. すべてのVCLはメインGUIスレッドで実行されるため、バックグラウンドスレッドから同じコードを呼び出す場合にのみロックが必要です。

  5. このコードを見ると、ワーカースレッドを産むのではなく、GUIスレッドで作業を行うことで同じことを達成できることがわかります。

私はすでにこのリンクをStackOverFlowに何度か投稿しましたが、でアドバイスをフォローすることを検討してください このリストの投稿 マルチスレッドプログラミングを行う際には、いくつかのことを念頭に置いてください。特に5番目のポイントに関していくつかの良いアドバイスがあります。

編集: 正確に言うのは難しいですが、あなたのコードはおそらく次のようなものでなければなりません:

procedure TForm1.ActionStartExecute(Sender: TObject);
begin
  ActionStart.Enabled := FALSE;
  fWorkerThread := TWorkerThread.Create (Handle, SomeLengthyServerOperation);
end;

procedure TForm1.ActionStartUpdate(Sender: TObject);
begin
  ActionStart.Enabled := fWorkerThread = nil;
end;

procedure TForm1.WMThreadFinished(var AMsg: TWMThreadFinishedMsg);
begin
  // process results
  fWorkerThread := nil;
end;

Tworkerthreadはそれ自体を解放しますが、最後のアクションとしてフォームにメッセージを投稿します(そのため、ウィンドウハンドルをパラメーターとして取得します)。このメッセージのハンドラーでは、fworkerthreadをnilに設定するため、次のアイドルループでアクションが再度有効になります。 TACTIONを使用すると、UI要素がスレッドの作成を発信するものを気にする必要がないことを意味します。スレッドが作成されるとすべて無効になり、スレッドが終了すると再度有効になります。ロックは必要ありませんし、スレッドがアクティブになっている間に新しいスレッドを作成することはできません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top