2012-01-27 3 views
13

年前、スレッドのFreeOnTerminateプロパティをtrueに設定してアプリケーションの終了時に2つの事柄を発見して推論したので、Thread.FreeOnTerminate:= True、メモリリーク、ゴースト実行

  1. それはメモリリークを生成し、プログラムの終了後
  2. 、スレッドはまだどこかに私のノートパソコンのキーボードの下に実行されています。

私は回避策に慣れていましたが、今回はすべて気にしませんでした。今夜まで、もう一度誰かが(この場合は@MartinJames)my answerにコメントしました。ここでは、FreeOnTerminateをスレッドの早すぎる終了と組み合わせて使用​​しないコードを参照しています。私はRTLコードに戻り、私が間違った前提を作ったかもしれないことに気付きました。しかし、私はそれについても、したがってこの疑問についてはあまりよく分かりません。

まず、上記の文を述べ再現するために、この例示的なコードが使用されます。

unit Unit3; 

interface 

uses 
    Classes, Windows, Messages, Forms; 

type 
    TMyThread = class(TThread) 
    FForm: TForm; 
    procedure Progress; 
    procedure Execute; override; 
    end; 

    TMainForm = class(TForm) 
    procedure FormClick(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    FThread: TMyThread; 
    end; 

implementation 

{$R *.dfm} 

{ TMyThread } 

procedure TMyThread.Execute; 
begin 
    while not Terminated do 
    begin 
    Synchronize(Progress); 
    Sleep(2000); 
    end; 
end; 

procedure TMyThread.Progress; 
begin 
    FForm.Caption := FForm.Caption + '.'; 
end; 

{ TMainForm } 

procedure TMainForm.FormClick(Sender: TObject); 
begin 
    FThread := TMyThread.Create(True); 
    FThread.FForm := Self; 
    FThread.FreeOnTerminate := True; 
    FThread.Resume; 
end; 

procedure TMainForm.FormDestroy(Sender: TObject); 
begin 
    FThread.Terminate; 
end; 

end. 

(状況A)は、フォーム上のクリックでスレッドを開始し、右の後、フォームを閉じた場合キャプションが変更された、68バイトのメモリリークがあります。これは、スレッドが解放されていないためです。次に、プログラムは直ちに終了し、IDEは同じ瞬間に再び正常状態に戻ります。 (状況B)とは対照的に、FreeOnTerminateを使用せず、上記のコードの最後の行をFThread.Freeに変更すると、プログラムが消滅してから通常のIDE状態に(最大)2秒かかります。

状況Bでの遅延は、FThread.FreeFThread.WaitForを呼び出し、両方ともメインスレッドのコンテキストで実行されるという事実によって説明されます。 Classes.pasのさらなる調査では、FreeOnTerminateによるスレッドの破棄はワーカースレッドのコンテキストで行われることが分かりました。これは、状況Aに関する次の質問につながります。

  • 本当にメモリリークがありますか?もしそうなら、それは重要です、無視することができますか?アプリケーションが終了すると、Windowsはすべての予約済みリソースを返還しないので、
  • スレッドはどうなりますか?その作業が完了するまで、それは実際にメモリのどこかでさらに実行されますか?そして:メモリリークの証拠にもかかわらず、それは解放されますか?

免責事項:メモリリーク検出では、私はthis very simple unitをプロジェクトファイルの最初に使用します。

+0

あなたがすでに読んでいるかどうかわかりませんが、私は[Raymond Chen](http://blogs.msdn。com/b/oldnewthing)のようなものです。これを例に挙げてください(http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx)または[2](http://blogs.msdn.com/ b/oldnewthing/archive/2007/05/02/2365433.aspx#2375204)。コメントやリンク先も見てください。 – EMBarbosa

答えて

12

実際、OSは終了時にすべてのプロセスのメモリを再利用します。したがって、68バイトが解放されていないスレッドオブジェクトを参照していても、OSはそれらのバイトを戻します。あなたがその時点でオブジェクトを解放したかどうかは本当に問題ではありません。

メインプログラムが終了すると、最終的にExitProcessと呼ばれる場所に到達します。 (プロジェクトのリンカオプションでデバッグDCUを有効にして、デバッガでその時点まで進めることができるはずです)。そのAPI呼び出しは、他のすべてのスレッドの終了を含むいくつかのことを行います。スレッドには終了が通知されないため、TThreadによって提供されるクリーンアップコードは決して実行されません。 OSスレッドは単に存在しなくなります。

+0

+1 OSはプロセスメモリの割り当てを解除する前にすべてのプロセススレッドを停止します。これはAVを取得しない理由であり、OSをクリーンアップするだけでメモリが安全です。 OSは、スレッドがExitProcess()を呼び出すスレッドとは別のコアで実行されていても、即座にすべてのスレッドを停止することができ、遅延なくアプリケーションをシャットダウンすることができます。もちろん、スレッドを明示的に停止しようとする必要がある場合があります。トランザクションがコミットする必要があるか、ファイルがフラッシュされた可能性があります。それ以外の場合、リーク警告は偽りです。常にそこにあり、決して成長しない場合、心配する必要はありません。 –

+0

最後の一点 - シャットダウン時にAV/sまたは216/217の例外ボックスが表示されますが、試してみる必要があります。 RTLがフォームを閉じ、ExitProcess()に達する前にフォームメモリを解放するので、シャットダウン時に解放されたメモリにアクセスしようとする可能性があります。私はちょうどそうしたことをしないので、問題はない。 –