1. 程式人生 > >慎用DelphiXE的TTask.WaitForAll/WaitForAny 一不小心會造成記憶體洩露!

慎用DelphiXE的TTask.WaitForAll/WaitForAny 一不小心會造成記憶體洩露!

      很多時候我們會用ttask.waitforall等待一組任務的結果,然後在主執行緒UI裡面報告執行結果, 因為waitforall方法是阻塞式的等待,如果直接在主執行緒裡執行,會卡死UI, 所以就嘗試開另一個task用來等待這組任務的結束,如下程式碼:

var
  aTask: array of ITask;
  i: integer;
begin
  sfLogger.logMessage('The start.');
  setlength(aTask, 10);
  for i := 0 to 9 do
  begin
    aTask[i] := TTask.Run(
      procedure
      var
        aValue: integer;
      begin
        aValue := Random(2000);
        sleep(aValue);
        tthread.Synchronize(nil,
          procedure
          begin
            mmo1.Lines.Add(aValue.ToString());
          end);
      end);
  end;
  TTask.Run(
    procedure
    begin
      TTask.WaitForAll(aTask);
      tthread.Synchronize(nil,
        procedure
        begin
          mmo1.Lines.Add('END.');
        end);
    end);
end;


 執行是很順利, 但當關閉應用後報告有記憶體出錯!

很明顯是這組TTask資源在執行過waitforall後就沒能成功釋放! 但如果在主執行緒裡面用waitforall,則完全沒這個問題!

所以建議自行用計算數記錄剩餘任務數的方法來自行處理等待吧!

下面是示例:

var
  aTask: array of ITask;
  i, lvC: integer;
begin
  sfLogger.logMessage('The start.');
  setlength(aTask, 10);
  lvC := 0;
  for i := 0 to 9 do
  begin
    AtomicIncrement(lvC);
    aTask[i] := TTask.Run(
      procedure
      var
        aValue: integer;
      begin
        aValue := Random(2000);
        sleep(aValue);
        tthread.Synchronize(nil,
          procedure
          begin
            mmo1.Lines.Add(aValue.ToString());
          end);
          AtomicDecrement(lvC);
      end);
  end;
  while lvC>0 do
    Application.ProcessMessages;
  mmo1.Lines.Add('END.');
  {TTask.Run(
    procedure
    begin
      TTask.WaitForAll(aTask);
      tthread.Synchronize(nil,
        procedure
        begin
          mmo1.Lines.Add('END.');
        end);
    end);}
end;


=======================

而具戲劇性的是,如果, 這個waitforall也不是一定不能放在task裡面等待, 如果將aTask[i]:=TTask.run(...)這個變成通過一個procedure來賦值就沒事:

procedure runtask(var aTask: ITask);
begin
  aTask := TTask.Run(
    procedure
    var
      aValue: integer;
    begin
      aValue := Random(2000);
      sleep(aValue);
      sfLogger.logMessage(aValue.ToString());
    end);
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  aTask: array of ITask;
  i: integer;
begin
  sfLogger.logMessage('The start.');
  setlength(aTask, 10);
  for i := 0 to 9 do
  begin
    // Sleep(1);
    runtask(aTask[i]);

  end;
  TTask.Run(
    procedure
    begin
      TTask.WaitForAll(aTask);
      tthread.Synchronize(nil,
        procedure
        begin
          mmo1.Lines.Add('END.');
        end);
    end);
end;

而runtask可以是procedure也可以是function, 但一定不能是巢狀函式,否則也會記憶體洩露.

這算不算XE7的大坑??!!不知道10.2有沒有修復此問題.