9

プログラムにメインスレッドと別のスレッドがあります。別のスレッドがメインスレッドの前に終了すると、自動的に解放されます。メインスレッドが最初に終了した場合は、別のスレッドを解放する必要があります。

FreeOnTerminateについて知っていますが、慎重に使用する必要があることを読みました。

私の質問は、次のコードは正しいですか?

procedure TMyThread.Execute;
begin
  ... Do some processing

  Synchronize(ThreadFinished);

  if Terminated then exit;

  FreeOnTerminate := true;
end;

procedure TMyThread.ThreadFinished;
begin
  MainForm.MyThreadReady := true;
end;

procedure TMainForm.Create;
begin
  MyThreadReady := false;

  MyThread := TMyThread.Create(false);
end;

procedure TMainForm.Close;
begin
  if not MyThreadReady then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;
end;
4

5 に答える 5

7

これを次のように簡略化できます。

procedure TMyThread.Execute;
begin
  // ... Do some processing
end;

procedure TMainForm.Create;
begin
  MyThread := TMyThread.Create(false);
end;

procedure TMainForm.Close;
begin
  if Assigned(MyThread) then
    MyThread.Terminate;
  MyThread.Free;
end;

説明:

  • スレッドを手動で使用FreeOnTerminateまたは解放しますが、両方を実行しないでください。スレッド実行の非同期性は、スレッドを解放しない、または(さらに悪いことに)2回実行するリスクを冒すことを意味します。実行が終了した後もスレッドオブジェクトを保持するリスクはありません。またTerminate()、すでに終了しているスレッドを呼び出すリスクもありません。

  • あるスレッドからのみ書き込まれ、別のスレッドから読み取られるブール値へのアクセスを同期する必要はありません。最悪の場合、間違った値を取得しますが、とにかく偽の効果である非同期実行が原因です。同期は、アトミックに読み取りまたは書き込みができないデータにのみ必要です。また、同期する必要がある場合は、使用しないSynchronize()でください。

  • スレッドの状態を調べるためにMyThreadReady使用できるので、のような変数を持つ必要はありません。最初のパラメーターと2番目のパラメーターとしてWaitForSingleObject()渡し、結果が-であるかどうかを確認します。そうである場合は、スレッドの実行が終了します。MyThread.Handle0WAIT_OBJECT_0

ところで:OnCloseイベントを使用せず、OnDestroy代わりに使用してください。前者は必ずしも呼び出されるわけではありません。その場合、スレッドは実行を継続し、プロセスを存続させる可能性があります。

于 2010-08-24T14:34:43.303 に答える
5

メインスレッドにハンドラーをワーカースレッドのOnTerminateイベントに割り当てさせます。ワーカースレッドが最初に終了した場合、ハンドラーはメインスレッドにスレッドを解放するように通知できます。メインスレッドが最初に終了すると、ワーカースレッドを終了できます。例えば:

procedure TMyThread.Execute;
begin
  ... Do some processing ...
end;

procedure TMainForm.Create;
begin
  MyThread := TMyThread.Create(True);
  MyThread.OnTerminate := ThreadFinished;
  MyThread.Resume; // or MyThread.Start; in D2010+
end;

const
  APPWM_FREE_THREAD = WM_APP+1;

procedure TMainForm.ThreadFinished(Sender: TObject);
begin
  PostMessage(Handle, APPWM_FREE_THREAD, 0, 0);
end;

procedure TMainForm.WndProc(var Message: TMessage);
begin
  if Message.Msg = APPWM_FREE_THREAD then
    StopWorkerThread
  else
    inherited;
end;

procedure TMainForm.StopWorkerThread;
begin
  if MyThread <> nil then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    FreeAndNil(MyThread);
  end;
end;

procedure TMainForm.Close;
begin
  StopWorkerThread;
end;
于 2010-08-24T17:06:03.480 に答える
2

いいえ、コードは適切ではありません(ただし、99.99%または100%の場合でも機能する可能性があります)。メインスレッドから作業スレッドを終了することを計画している場合は、FreeOnTerminateをTrueに設定しないでください(FreeOnTerminateをTrueに設定することで、上記のコードで何を得ようとしているのかわかりません。少なくともコードが理解しにくくなります) 。

作業スレッドを終了する際のより重要な状況は、作業スレッドが待機状態にあるときにアプリケーションを閉じようとしていることです。Terminateを呼び出すだけではスレッドは起動されません。通常、追加の同期オブジェクト(通常はイベント)を使用して作業スレッドを起動する必要があります。

そしてもう1つの発言-必要はありません

  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;

TThread.Destroyコードを見ると、TerminateとWaitForが呼び出されるため、

    MyThread.Free;

十分です(少なくともDelphi 2009では、チェックするDelphi 7ソースが手元にありません)。


更新しました

mghieの答えを読んでください。次の状況を考慮してください(1 CPUシステムの方が良い):

メインスレッドが実行されています

procedure TMainForm.Close;
begin
  if not MyThreadReady then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;
end;

MyThreadReady値(False)をチェックし、スケジューラーによってオフにされました。

これで、スケジューラーが作業スレッドに切り替わります。実行します

  Synchronize(ThreadFinished);

スケジューラーを強制的にメインスレッドに戻します。メインスレッドは実行を継続します:

    MyThread.Terminate;   // no problem
    MyThread.WaitFor;     // ???
    MyThread.Free;

WaitForで何が起こるかを言うことができますか?答えることはできません(答えるにはTThreadソースを詳しく調べる必要がありますが、一見デッドロックのように見えます)。

あなたの本当のエラーは何か違うものです-あなたは信頼できないコードを書き、それが正しいかどうかを調べようとしています。これはスレッドでは悪い習慣です。代わりに、信頼できるコードを書くことを学ぶ必要があります。

リソースに関しては、TThread(FreeOnTerminate = False)が終了した場合、割り当てられたままのリソースは、Windowsスレッドハンドル(スレッドが終了した後は実質的なWindowsリソースを使用しません)とメモリ内のDelphiTThreadオブジェクトのみです。安全側にいることは大きなコストではありません。

于 2010-08-24T14:17:19.327 に答える
0

正直なところ、あなたの


... Do some processing

ここでの本当の問題です。それは再帰的に何かをするためのループですか?そうでない場合、代わりに、それは巨大なタスクです。このタスクを小さなプロシージャ/関数に分割し、すべてを実行本体にまとめて、次のようにスレッドの状態を知るために条件付きifを使用して次々に呼び出すことを検討する必要があります。

 

While not Terminated do
 begin

  if MyThreadReady then
    DoStepOneToTaskCompletion
  else
    clean_and_or_rollback(Something Initialized?);

  if MyThreadReady then
    DoStepTwoToTaskCompletion
  else
    clean_and_or_rollback(Something Initialized?, StepOne);

  if MyThreadReady then
    DoStepThreeToTaskCompletion
  else
    clean_and_or_rollback(Something Initialized?, StepOne, StepTwo);

  Self.DoTerminate; // Not sure what to expect from that one
 end;

汚れていて、ほとんどハックですが、期待どおりに機能します。

FreeOnTerminateについては、まあ...宣言を削除するだけで常に


FreeAndNil(ThreadObject);

私はsyncroniseのファンではありません。より多くの共有データを処理するためにコードを拡張する柔軟性のために、よりクリティカルなセクションが好きです。

フォームの公開セクションで、次のことを宣言します。

ControlSection : TRTLCriticalSection;

フォームの作成時またはthread.createの前のどこかで、

InitializeCriticalSection(ControlSection);

次に、共有リソース(MyThreadReady変数を含む)に書き込むたびに、


EnterCriticalSection ( ControlSection );
  MyThreadReady := True; //or false, or whatever else
LeaveCriticalSection ( ControlSection );

行く(出る)前に、電話してください


DeleteCriticalSection ( ControlSection );

いつものようにスレッドを解放します。

よろしくラファエル

于 2010-08-24T22:33:36.857 に答える
0

私は、モデルの混合は単に推奨されないと述べます。FreeOnTerminateを使用して、スレッドに二度と触れないか、触れないかのどちらかです。それ以外の場合は、2人が通信するための保護された方法が必要です。

スレッド変数を細かく制御する必要があるため、FreeOnTerminateを使用しないでください。スレッドが早期に終了する場合は、通常どおりにスレッドが消費したローカルリソースをクリアし、アプリケーションの終了時にメインスレッドが子スレッドを解放するようにします。両方の長所を活用できます。子スレッドによってリソースができるだけ早く解放され、スレッドの同期について心配する必要はありません。(そして、デザイン/コード/理解/サポートがはるかにシンプルになるという追加のボーナスがあります...)

于 2010-08-25T00:56:42.697 に答える