情况
为了更好地理解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)
我认为我可以添加此代码以提高程序的安全性.够了吗?
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)
| 归档时间: |
|
| 查看次数: |
7967 次 |
| 最近记录: |