9

TTimer.OnTimerイベントで6つの匿名スレッドを生成するDelphiアプリケーションがあります。

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

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

私のために働いた解決策:

Free-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;

メモリリークがなく、適切にシャットダウンされ、使いやすい。

4

3 に答える 3

16

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

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

スレッドは FreeOnTerminate としてもマークされているため、Start を呼び出した後に返されたインスタンスに触れないでください。

したがって、デフォルトでは、スレッドの存続期間を制御する立場にはありません。FreeOnTerminateただし、をFalse呼び出す直前に を設定できますStart。このような:

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

しかし、私がそうするかどうかはわかりません。の設計でCreateAnonymousThreadは、終了時にスレッドが自動的に解放されます。TThread個人的には、意図したデザインに従うか、自分の子孫を派生させるかのどちらかだと思います。

于 2012-05-01T18:31:50.743 に答える
9

エラーを回避するには、開始する前にCreateAnonymousThreadset FreeOnTerminatetoを使用します。False

このようにして、回避策なしで通常どおりにスレッドを操作できます。

CreateAnonymousThreadが自動的に設定FreeOnTerminateされるというドキュメントを読むことができますTrue。これが、スレッドを参照するときにエラーを引き起こしている原因です。

于 2013-03-10T23:54:25.093 に答える
3

スレッドに外部からの何らかの通知を監視させます。これは、シグナルを受け取るイベント、スレッドが所有するウィンドウに送信されるメッセージ、スレッドがリッスンするソケットを介して送信されるコマンド、またはその他の通信形式である可能性があります。

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

于 2012-05-01T18:28:54.077 に答える