gpt4 book ai didi

Delphi任务的正确使用

转载 作者:行者123 更新时间:2023-12-03 14:57:46 25 4
gpt4 key购买 nike

情况

为了更好地理解 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;

您在上面看到的文件夹maths并不是很大,任务执行大约需要3秒。任务声明如下:

type
TForm3 = class(TForm)
ListBox1: TListBox;
//... other published things var ...
private
proc: ITask;
public
//... public var ...
end;
<小时/>

问题

例如,当我使用 C:\Users\albertoWinVM\Documents 时,我有大量文件夹,程序最多需要 3 分钟才能填充列表框。

如果我关闭程序(当任务仍在运行时)只有上面的代码,根据我在网上阅读的理解,任务仍然会运行,直到他没有完成。我说得对吗?

procedure TForm3.FormDestroy(Sender: TObject);
begin
proc.Cancel;
end;

我想我可以添加这段代码来提高程序的安全性。这样就够了吗?

最佳答案

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

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

不,仅在 TTask 上调用 Cancel() 是不够的。您的任务过程需要定期检查任务是否已被取消,以便它可以停止其工作(取消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
AddToListBox(batch);
batch := nil;
numInBatch := 0;
end;
end;
end;
until FindNext(sr) <> 0;
finally
FindClose(sr);
end;

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

procedure TForm3.AddToListBox(batch: TStringDynArray);
begin
TThread.Queue(nil,
procedure
begin
ListBox1.Items.AddStrings(batch);
end
end);
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;

关于Delphi任务的正确使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39577157/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com