Delphi正确使用任务

Alb*_*ola 6 delphi

情况

为了更好地理解PPL以及如何Task作品我试图做一个非常简单的程序中,一旦你点击一个按钮,一个ListBox充满磁盘目录列表.

procedure TForm3.Button1Click(Sender: TObject);
var proc: ITask;
begin

 //Show that something is going to happen
 Button1.Caption := 'Process...';

 proc := TTask.Create(

  procedure
  var strPath: string;
      sl: TStringDynArray;
  begin

   if (DirectoryExists('C:\Users\albertoWinVM\Documents\uni\maths')) then
    begin
     ListBox1.Items.Clear;
     sl := TDirectory.GetDirectories('C:\Users\albertoWinVM\Documents\uni\maths',
     TSearchOption.soAllDirectories, nil);

     for strPath in sl do
      begin
       ListBox1.Items.Add(strPath);
      end;

     //At the end of the task, I restore the original caption of the button 
     Button1.Caption := 'Go';
     Label1.Caption := 'Finished';

    end;
  end

 );

 proc.Start;

end;
Run Code Online (Sandbox Code Playgroud)

maths您可以在上面看到的文件夹不是很大,任务大约需要3秒钟才能执行.任务声明如下:

type
  TForm3 = class(TForm)
    ListBox1: TListBox;
    //... other published things var ...
  private
    proc: ITask;
  public
    //... public var ...
  end;
Run Code Online (Sandbox Code Playgroud)

问题

当我工作(例如)时,C:\Users\albertoWinVM\Documents我有很多文件夹,程序在填充ListBox之前需要3分钟.

如果我关闭程序(当任务仍在运行时)只有上面的代码,从我理解的在线阅读,任务仍然会运行,直到他还没有完成.我对么?

procedure TForm3.FormDestroy(Sender: TObject);
begin
 proc.Cancel;
end;
Run Code Online (Sandbox Code Playgroud)

我认为我可以添加此代码以提高程序的安全性.够了吗?

Rem*_*eau 9

TTask在工作线程中运行.如图所示,您的任务代码不是线程安全的.访问UI控件时,您必须与主UI线程同步.

您没有proc正确管理变量.您有一个proc声明为您的TForm3类成员的变量,但您还在方法中proc声明了一个局部变量Button1Click().该方法将新任务分配给局部变量,永远不会分配类成员.

不,仅仅呼唤它是不够Cancel()TTask.您的任务过程需要定期检查任务是否已被取消,以便它可以停止工作(取消的唯一方法TDirectory.GetDirectories()是让其谓词过滤器引发异常).

由于TDirectory.GetDirectories()直到所有目录都已定位并存储在返回的列表中才会退出,如果您需要更负责任的任务和更快的UI结果,或者您只是想减少内存使用,则应该在手动循环中使用FindFirst()/ FindNext(),然后你可以根据需要更新UI并在循环迭代之间检查取消.

话虽如此,尝试更像这样的东西:

type
  TForm3 = class(TForm)
    ListBox1: TListBox;
    //...
  private
    proc: ITask;
    procedure AddToListBox(batch: TStringDynArray);
    procedure TaskFinished;
  public
    //...
  end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  if Assigned(proc) then
  begin
    ShowMessage('Task is already running');
    Exit;
  end;

  //Show that something is going to happen
  Button1.Caption := 'Process...';

  proc := TTask.Create(
    procedure
    var
      strFolder: string;
      sr: TSearchRec;
      batch: TStringDynArray;
      numInBatch: Integer;
    begin
      try
        strFolder := 'C:\Users\albertoWinVM\Documents\uni\maths\';
        if FindFirst(strFolder + '*.*', faAnyFile, sr) = 0 then
        try
          TThread.Queue(nil, ListBox1.Items.Clear);
          batch := nil;

          repeat
            Form3.proc.CheckCanceled;

            if (sr.Attr and faDirectory) <> 0 then
            begin
              if (sr.Name <> '.') and (sr.Name <> '..') then
              begin
                if not Assigned(batch) then
                begin
                  SetLength(batch, 25);
                  numInBatch := 0;
                end;

                batch[numInBatch] := strFolder + sr.Name;
                Inc(numInBatch);

                if numInBatch = Length(batch) then
                begin
                  TThread.Queue(nil,
                    procedure
                    begin
                      AddToListBox(batch);
                    end
                  end);

                  batch := nil;
                  numInBatch := 0;
                end;
              end;
            end;
          until FindNext(sr) <> 0;
        finally
          FindClose(sr);
        end;

        if numInBatch > 0 then
        begin
          SetLength(batch, numInBatch)
          TThread.Queue(nil,
            procedure
            begin
              AddToListBox(batch);
            end
          end);
        end;
      finally
        TThread.Queue(nil, TaskFinished);
      end;
    end
  );
  proc.Start;
end;

procedure AddToListBox(batch: TStringDynArray);
begin
  ListBox1.Items.AddStrings(batch);
end;

procedure TForm3.TaskFinished;
begin
  proc := nil;
  Button1.Caption := 'Go';
  Label1.Caption := 'Finished';
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
  if Assigned(proc) then
  begin
    proc.Cancel;
    repeat
      if not proc.Wait(1000) then
        CheckSynchronize;
    until proc = nil;
  end;
end;
Run Code Online (Sandbox Code Playgroud)