0

したがって、次のように THandle を使用してスレッド化を実装しました。

procedure Calc_Prin;
type
  TTeste = record
    ptrClass: TSpAu;
    ptrTEMPO: ^integer;
  end;

var
  TEMPO: integer;
  RESULTADO: THandle;
  thrID: DWord;
  teste: TTeste;

  function THREAD_C(PTR: pointer): longint; stdcall;
  begin
    try
      CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
      TPtrTeste(PTR).ptrClass.Calc;
      TPtrTeste(PTR).ptrTEMPO^ := 1;
    finally
      ExitThread(1);
      CoUninitialize;
      result := 0;
    end;
  end;

begin
  RESULTADO := CreateThread(nil, 0, @THREAD_C, @teste, 0, thrID);
  WaitForSingleObject(RESULTADO, TEMPO_PERMITIDO); 

  SuspendThread(RESULTADO);
  CloseHandle(RESULTADO);
end;

スレッドが正常になると (タイムアウトに達せず、プロセスが途中で終了することもありません)、リークはありませんが、スレッドに何らかの問題があり、タイムアウトに達すると、多くのリークが発生します。推測すると、すべての試行を無視して、関数から離れてしまいます..最後に、すべてを解放します。

スレッドをファイナライズし、それが許すリークを殺す方法はありますか?

4

2 に答える 2

2

スレッドをすぐに強制終了する ExitThread() を呼び出しています。これは、 CoUninitialize() 呼び出しが実行されなくなったことを意味します。自分で ExitThread を呼び出す必要はありません。スレッド関数を正常に終了するだけで十分です。

try
  // ...
finally
  CoUnintialize;
  Result := 1; // the value that you specified in the ExitThread() call
end;

SuspendThread() を呼び出すと、スレッドが一時停止することがありますが、「finally」ブロックは実行されず、Calc() 関数から離れることも、スレッドを終了することもありません。スレッドが正常に終了できるように、Calc() 関数に「has-terminated」チェックを追加する必要があります。

編集:
これは、潜在的なタイムアウトを知るために Calc() メソッドを変更することにより、スレッドを正常に終了できるようにする疑似コードです。

type
  ECalcTimedOut = class(Exception);

  TSpAu = class(...)
  protected
    FCalcTimedOut: Boolean;
    procedure CheckCalcTimedOut;
  end;

  PTeste = ^TTeste;
  TTeste = record
    ptrClass: TSpAu;
    ptrTEMPO: ^integer;
  end;

function THREAD_CALCULO(PTR: pointer): longint; stdcall;
begin
  CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
  try
    try
      PTeste(PTR).ptrClass.Calc;
      PTeste(PTR).ptrTEMPO^ := 1;
      Result := 1;
    except
      on ECalcTimedOut do
        Result := 0;
    end;
  finally
    CoUninitialize;
  end;
end;

procedure Calc_Prin;
var
  TEMPO: integer;
  RESULTADO: THandle;
  thrID: DWord;
  teste: TTeste;
begin
  // ...
  teste.ptrClass.FCalcTimedOut := False;

  RESULTADO := CreateThread(nil, 0, @THREAD_CALCULO, @teste, 0, thrID);
  if WaitForSingleObject(RESULTADO, TEMPO_PERMITIDO) = WAIT_TIMEOUT then
  begin
    // Signal the Calc() method that it timed out
    teste.ptrClass.FCalcTimedOut := True;
    // Wait for the thread to terminate gracefully
    WaitForSingleObject(RESULTADO, INFINITE);
  end;
  CloseHandle(RESULTADO);
end;

procedure TSpAu.CheckCalcTimedOut;
begin
  if FCalcTimedOut then
    raise ECalcTimedOut.Create('Calc Timed out');
end;

procedure TSpAu.Calc;
begin
  CheckCalcTimeout;
  // do something
  while condition do
  begin
    CheckCalcTimeout;
    DoSomethingElse;
    CheckCalcTimeout;
    // do something
  end;
end;

procedure TSpAu.DoSomethingElse;
begin
  for I := 0 to 1000000 do
  begin
    CheckCalcTimeout;
    // do something
  end;
end;
于 2014-09-26T19:27:50.563 に答える
0

代わりに、次のようなことを試してください。

type
  TSpAu = class
  public
    Cancelled: Boolean;
    procedure Calc;
  end;

  TPtrTeste = ^TTeste;
   TTeste = record
    ptrClass: TSpAu;
    ptrTEMPO: ^integer;
  end;

procedure TSpAu.Calc;
begin
  ...
  if Cancelled then Abort;
  ...
  if Cancelled then Abort;
  ...
end;


function THREAD_CALCULO(PTR: pointer): DWORD; stdcall;
begin
  CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
  try
    with TPtrTeste(PTR)^ do
    begin
      try
        ptrClass.Calc;
        ptrTEMPO^ := 1;
      except
        ptrTEMPO^ := 0;
      end;
    end;
  finally
    CoUninitialize;
  end;
  Result := 0;
end;

procedure Calc_Prin;
var
  TEMPO: integer;
  RESULTADO: THandle;
  thrID: DWord;
  teste: TTeste;
  ret: DWORD;
begin
  TEMPO := 0;

  teste.ptrClass := ...; // <-- whatever your TSpAu object is
  teste.ptrTEMPO := @TEMPO;

  RESULTADO := CreateThread(nil, 0, @THREAD_CALCULO, @teste, 0, thrID);
  if RESULTADO = 0 then RaiseLastOSError;
  try
    ret := WaitForSingleObject(RESULTADO, TEMPO_PERMITIDO);
    if ret = WAIT_TIMEOUT then
    begin
      teste.ptrClass.Cancelled := True;
      ret := WaitForSingleObject(RESULTADO, INFINITE);
    end;
    if ret = WAIT_FAILED then RaiseLastOSError;
  finally
    CloseHandle(RESULTADO);
  end;

  // use TEMPO as needed...
end;
于 2014-09-26T20:11:30.683 に答える