8

与えられた例では、電話をかけると例外が発生しますAThread.Free.

program Project44;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, Windows;

type
  TMyException = class(Exception);

var
  AThread: TThread;
begin
  AThread := TThread.Create(True);
  try
    AThread.FreeOnTerminate := True;
    //I want to do some things here before starting the thread
    //During the setup phase some exception might occur, this exception is for simulating purpouses
      raise TMyException.Create('exception');
  except
    AThread.Free; //Another exception here
  end;
end.

私は2つの質問があります:

  1. AThread特定の例でのインスタンスを解放するにはどうすればよいTThreadですか?

  2. わからない、なぜ自分自身を破壊する前にTThread.Destroy電話をかけているのか。Resumeこれのポイントは何ですか?

4

2 に答える 2

15

スレッドインスタンスに設定FreeOnTerminateTrue 呼び出すことはできません。Freeどちらか一方を実行する必要がありますが、両方を実行する必要はありません。現状では、コードはスレッドを2回破棄します。オブジェクトを2回破棄してはなりません。もちろん、デストラクタを2回実行すると、エラーが発生します。

ここで何が起こるかというと、スレッドを一時停止して作成したので、スレッドを明示的に解放するまで何も起こりません。これを行うと、デストラクタはスレッドを再開し、スレッドが完了するのを待ちます。に設定したため、これによりFree再度呼び出されます。この2回目の呼び出しで、ハンドルを閉じます。次に、スレッドprocに戻り、それが。を呼び出します。スレッドのハンドルが閉じられているため、これは失敗します。FreeOnTerminateTrueFreeExitThread

マーティンがコメントで指摘しているように、メソッドは抽象的でTThreadあるため、直接 作成してはなりません。また、廃止されたものはTThread.Execute使用しないでください。中断されたスレッドの実行を開始するためにResume使用します。Start

個人的には使いたくないFreeOnTerminateです。この機能を使用すると、作成元の別のスレッドでスレッドが破棄されます。通常、インスタンス参照を忘れたい場合に使用します。そのため、プロセスの終了時にスレッドが破棄されたかどうか、またはプロセスの終了中にスレッドが終了して解放されているかどうかについても不確かになります。

を使用する必要がある場合は、に設定した後にFreeOnTerminate電話をかけないようにする必要があります。したがって、明らかな解決策は、呼び出しの直前に設定してから、スレッドインスタンスを忘れることです。開始する準備が整う前に例外が発生した場合は、その時点でスレッドを安全に解放できます。FreeFreeOnTerminateTrueFreeOnTerminateTrueStartFreeOnTerminateFalse

Thread := TMyThread.Create(True);
Try
  //initialise thread object
Except
  Thread.Free;
  raise;
End;
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;

より洗練されたアプローチは、すべての初期化をTMyThreadコンストラクターに移動することです。その場合、コードは次のようになります。

Thread := TMyThread.Create(True);
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;
于 2012-01-10T15:44:47.020 に答える
5

あなたの場合、状況は非常に複雑です。

まず、中断されたスレッドを実際に解放するわけではありません。スレッドはデストラクタで再開されます:

  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;

Terminateが前に呼び出されるためResumeExecuteメソッドは実行されず、スレッドは再開された直後に終了します。

  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    Result := Thread.FReturnValue;
    FreeThread := Thread.FFreeOnTerminate;
    Thread.DoTerminate;
    Thread.FFinished := True;
    SignalSyncEvent;
    if FreeThread then Thread.Free;

最後の行を見てくださいThread.Free。デストラクタ自体からデストラクタ()を呼び出します。素晴らしいバグ!


あなたの質問に答えるには:

  1. FreeOnTerminate:= Trueコードで使用することはできません。
  2. TThreadがそのように設計されている理由をEmbarcaderoに尋ねる必要があります。私の推測-スレッドが終了している間、いくつかのコード(DoTerminateメソッド)をスレッドコンテキストで実行する必要があります。

機能リクエストをQCに送信できます:実装FFreeOnTerminate:= Falseに追加:TThread.Destroy

destructor TThread.Destroy;
begin
  FFreeOnTerminate:= False;
// everything else is the same
  ..
end;

これにより、再帰的な記述子の呼び出しが防止され、コードが有効になります。

于 2012-01-10T16:31:40.557 に答える