2012-05-01 53 views
7

私はいくつかのTTimer.OnTimerイベントで6つの匿名スレッドを生成するDelphiアプリケーションを持っています。アプリケーション終了時にDelphiで匿名スレッドを終了するにはどうすればよいですか?

タイトルバーのXボタンからアプリケーションを閉じると、アドレス$ C0000005のアクセス違反が発生し、FastMMはTAnonymousThreadオブジェクトを漏洩したと報告します。

TThread.CreateAnonymousThread()メソッドを使用してOnTimerイベント内で作成されたDelphiで匿名スレッドを解放する最も良い方法は何ですか?私のために働いた

SOLUTION:

無料-EDされると、それらを終了し、匿名のスレッドのラッパーを作成しました。ここ

type 
    TAnonumousThreadPool = class sealed(TObject) 
    strict private 
    FThreadList: TThreadList; 
    procedure TerminateRunningThreads; 
    procedure AnonumousThreadTerminate(Sender: TObject); 
    public 
    destructor Destroy; override; final; 
    procedure Start(const Procs: array of TProc); 
    end; 

{ TAnonumousThreadPool } 

procedure TAnonumousThreadPool.Start(const Procs: array of TProc); 
var 
    T: TThread; 
    n: Integer; 
begin 
    TerminateRunningThreads; 

    FThreadList := TThreadList.Create; 
    FThreadList.Duplicates := TDuplicates.dupError; 

    for n := Low(Procs) to High(Procs) do 
    begin 
    T := TThread.CreateAnonymousThread(Procs[n]); 
    TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID); 
    T.OnTerminate := AnonumousThreadTerminate; 
    T.FreeOnTerminate := true; 
    FThreadList.LockList; 
    try 
     FThreadList.Add(T); 
    finally 
     FThreadList.UnlockList; 
    end; 
    T.Start; 
    end; 
end; 

procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject); 
begin 
    FThreadList.LockList; 
    try 
    FThreadList.Remove((Sender as TThread)); 
    finally 
    FThreadList.UnlockList; 
    end; 
end; 

procedure TAnonumousThreadPool.TerminateRunningThreads; 
var 
    L: TList; 
    T: TThread; 
begin 
    if not Assigned(FThreadList) then 
    Exit; 
    L := FThreadList.LockList; 
    try 
    while L.Count > 0 do 
    begin 
     T := TThread(L[0]); 
     T.OnTerminate := nil; 
     L.Remove(L[0]); 
     T.FreeOnTerminate := False; 
     T.Terminate; 
     T.Free; 
    end; 
    finally 
    FThreadList.UnlockList; 
    end; 
    FThreadList.Free; 
end; 

destructor TAnonumousThreadPool.Destroy; 
begin 
    TerminateRunningThreads; 
    inherited; 
end; 

エンドは、あなたがそれを呼び出すことができる方法です。

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    FAnonymousThreadPool.Start([ // array of procedures to execute 
    procedure{anonymous1}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:\1.jpg'); 
     finally 
     Http.Free; 
     end; 
    end, 

    procedure{anonymous2}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:\2.jpg'); 
     finally 
     Http.Free; 
     end; 
    end 
    ]); 
end; 

ませんメモリリーク、適切なシャットダウンと使いやすいです。

+1

'匿名スレッド' - 大変です。エンバカデロが今私たちに求めてきたことは? –

+1

@マルティン:何も怖い、本当に。これは、作成時に匿名メソッドによって動作が提供されるスレッドです。スレッドを定義するときにクロージャを使用できます。 –

+0

スレッドを継続的に作成/破棄しています。過去20年を費やして、開発者に避けるよう指示してきました。それにもかかわらず、実際に開始されていない場合、私はなぜAVがあるべきかを見ることができません。 –

答えて

14

スレッドの有効期間を維持し、制御する場合は、FreeOnTerminateFalseに設定する必要があります。それ以外の場合、実行を開始した後にスレッドを参照するのはエラーです。それが実行されると、解放されたかどうかを知る準備ができていないからです。

CreateAnonymousThreadを呼び出すと、FreeOnTerminateTrueに設定されたスレッドが作成されます。

The thread is also marked as FreeOnTerminate, so you should not touch the returned instance after calling Start.

デフォルトでは、スレッドの有効期間を制御することはできません。ただし、Startを呼び出す直前にFreeOnTerminateFalseに設定することができます。このように:

MyThread := TThread.CreateAnonymousThread(MyProc); 
MyThread.FreeOnTerminate := False; 
MyThread.Start; 

しかし、私はそれを行うだろうか分からない。 CreateAnonymousThreadのデザインは、終了時にスレッドが自動的に解放されるということです。私は個人的には意図したデザインに従うか、自分のTThread子孫を引き出すだろうと思う。

+0

私は参照してください。しかし、私はまだそれを取得しません。 TAnonymousThreadはTThreadの子孫です。だから私はそれらのリストを維持し、それらを終了しようとする(RTLは自動的にそれをしないので)。私が終了しようとすると言う:プロジェクトmtgstudio.exe例外クラスEThreadメッセージ 'スレッドエラー:ハンドルが無効です(6)'を発生させます。 –

+0

匿名スレッドのドキュメントを読んでください。 FreeOnTerminateを使用しているため、TThreadへの参照を保持することはできません。 –

+0

申し訳ありませんが、私はこのプロジェクトを見ることができません。あなたが尋ねた質問に私は答えたと思う。基本的に、匿名スレッドを開始すると、そのスレッドを制御できなくなります。もうそれを参照することはできません。 –

3

スレッドは、外部から何らかの通知を受けるようにします。これは、シグナルを受け取るイベント、スレッドが所有するウィンドウに送信されたメッセージ、スレッドが待機するソケットを介して送信されたコマンド、その他の通信の形式などです。

スレッドがいわゆる「匿名」スレッドであることが原因であると判断した場合、簡単な回避策は、匿名以外のスレッドにすることです。匿名関数の本体をExecuteメソッドに入れ、キャプチャされた変数をそのコンストラクタ経由でスレッドクラスに渡します。

+0

匿名メソッドをTThread子孫に簡単に渡して、Executeメソッドでそれを実行することができます。 –

+0

もちろん、@ David。 'CreateAnonymousThread'はそれを証明していませんか?だから何? –

+1

私は、それがあなたの答えの最後の文で示唆されたアプローチの代わりになることを意味します。 –

8

CreateAnonymousThreadを使用してエラーを回避するには、開始する前にFreeOnTerminateFalseと設定してください。

このようにすれば、通常どおり回避することなくスレッドを操作できます。

あなたはCreateAnonymousThreadが自動的TrueからFreeOnTerminateを設定しますと言っているドキュメントを読むことができ、これはあなたがスレッドを参照する場合、エラーを引き起こしているものです。

関連する問題