如何从外部检查ITask 线程是否仍然运行?
How to check from outside whether ITask thread is still running?
在Delphi XE7(或XE8)中,将TjvProgressDialog(来自JVCL)放在一个窗体上并将其命名为dlgProgress1.还有一个 TButton 并将其命名为 btnProgressDialogTest.
此代码首先从一个单独的线程 (ShellExecAndWaitTask) 启动记事本,然后打开带有无限进度循环的进度对话框 (dlgProgress1):
var
ShellExecAndWaitTask: System.Threading.ITask;
procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
CodeSite.Send('Notepad has been closed');
end);
ShellExecAndWaitTask.Start;
dlgProgress1.Caption := 'ProgressDialog Test';
dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
dlgProgress1.Tag := 0;
dlgProgress1.Position := 0;
dlgProgress1.ShowCancel := True;
dlgProgress1.ShowModal;
CodeSite.Send('Progress dialog has been closed');
end;
procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
if dlgProgress1.Tag = 0 then
begin
if dlgProgress1.Position < dlgProgress1.Max then
dlgProgress1.Position := dlgProgress1.Position + 1;
if dlgProgress1.Position = dlgProgress1.Max then
dlgProgress1.Tag := 1;
end
else
begin
if dlgProgress1.Position > 0 then
dlgProgress1.Position := dlgProgress1.Position - 1;
if dlgProgress1.Position = 0 then
dlgProgress1.Tag := 0;
end;
AContinue := Assigned(ShellExecAndWaitTask); // why this never becomes false?
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(ShellExecAndWaitTask) then
ShellExecAndWaitTask.Cancel;
end;
当您关闭记事本时,dlgProgress1Progress
事件处理程序中的 Assigned(ShellExecAndWaitTask)
是否应该变为 false 并通过将 AContinue
设置为 false 来关闭进度对话框?相反,尽管 ShellExecAndWaitTask
任务已终止,它始终保持为真!为什么?
编辑:
我按照 David 的建议更改了代码。现在它可以工作了,但是它是线程安全的吗?
var
ShellExecAndWaitTask: System.Threading.ITask;
ShellExecAndWaitTaskTerminated: Boolean;
procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
CodeSite.Send('Notepad has been closed');
TThread.Queue(TThread.CurrentThread,
procedure
begin
ShellExecAndWaitTaskTerminated := True;
end);
end);
ShellExecAndWaitTaskTerminated := False;
ShellExecAndWaitTask.Start;
dlgProgress1.Caption := 'ProgressDialog Test';
dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
dlgProgress1.Tag := 0;
dlgProgress1.Position := 0;
dlgProgress1.ShowCancel := True;
dlgProgress1.ShowModal;
CodeSite.Send('Progress dialog has been closed');
end;
procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
if dlgProgress1.Tag = 0 then
begin
if dlgProgress1.Position < dlgProgress1.Max then
dlgProgress1.Position := dlgProgress1.Position + 1;
if dlgProgress1.Position = dlgProgress1.Max then
dlgProgress1.Tag := 1;
end
else
begin
if dlgProgress1.Position > 0 then
dlgProgress1.Position := dlgProgress1.Position - 1;
if dlgProgress1.Position = 0 then
dlgProgress1.Tag := 0;
end;
AContinue := not ShellExecAndWaitTaskTerminated;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(ShellExecAndWaitTask) then
ShellExecAndWaitTask.Cancel;
end;
When you close Notepad, shouldn't Assigned(ShellExecAndWaitTask) in the dlgProgress1Progress event handler become false and close the progress dialog by setting AContinue to false?
不,事情从来都不是这样的。例如,您可以有许多引用任务的不同变量。您怎么能期望所有这些变量都设置为 nil
.
不,接口引用变量保持赋值状态,直到它离开作用域,或者您将其设置为 nil
。顺便说一句,我严重质疑您为此使用全局变量的选择。我不知道你为什么做出那个决定,但这似乎是个错误的决定。
最简单的方法就是在调用 ShellExecAndWait
returns 时发送消息。
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad');
CodeSite.Send('Notepad has been closed');
// notify the main thread that the external process has completed
end);
例如,您可以使用 PostMessage
或 TThread.Queue
或 TThread.Synchronize
来通知主线程。
在Delphi XE7(或XE8)中,将TjvProgressDialog(来自JVCL)放在一个窗体上并将其命名为dlgProgress1.还有一个 TButton 并将其命名为 btnProgressDialogTest.
此代码首先从一个单独的线程 (ShellExecAndWaitTask) 启动记事本,然后打开带有无限进度循环的进度对话框 (dlgProgress1):
var
ShellExecAndWaitTask: System.Threading.ITask;
procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
CodeSite.Send('Notepad has been closed');
end);
ShellExecAndWaitTask.Start;
dlgProgress1.Caption := 'ProgressDialog Test';
dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
dlgProgress1.Tag := 0;
dlgProgress1.Position := 0;
dlgProgress1.ShowCancel := True;
dlgProgress1.ShowModal;
CodeSite.Send('Progress dialog has been closed');
end;
procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
if dlgProgress1.Tag = 0 then
begin
if dlgProgress1.Position < dlgProgress1.Max then
dlgProgress1.Position := dlgProgress1.Position + 1;
if dlgProgress1.Position = dlgProgress1.Max then
dlgProgress1.Tag := 1;
end
else
begin
if dlgProgress1.Position > 0 then
dlgProgress1.Position := dlgProgress1.Position - 1;
if dlgProgress1.Position = 0 then
dlgProgress1.Tag := 0;
end;
AContinue := Assigned(ShellExecAndWaitTask); // why this never becomes false?
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(ShellExecAndWaitTask) then
ShellExecAndWaitTask.Cancel;
end;
当您关闭记事本时,dlgProgress1Progress
事件处理程序中的 Assigned(ShellExecAndWaitTask)
是否应该变为 false 并通过将 AContinue
设置为 false 来关闭进度对话框?相反,尽管 ShellExecAndWaitTask
任务已终止,它始终保持为真!为什么?
编辑:
我按照 David 的建议更改了代码。现在它可以工作了,但是它是线程安全的吗?
var
ShellExecAndWaitTask: System.Threading.ITask;
ShellExecAndWaitTaskTerminated: Boolean;
procedure TForm1.btnProgressDialogTestClick(Sender: TObject);
begin
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad'); // BTW, is this thread-safe?
CodeSite.Send('Notepad has been closed');
TThread.Queue(TThread.CurrentThread,
procedure
begin
ShellExecAndWaitTaskTerminated := True;
end);
end);
ShellExecAndWaitTaskTerminated := False;
ShellExecAndWaitTask.Start;
dlgProgress1.Caption := 'ProgressDialog Test';
dlgProgress1.Text := 'Close Notepad to automatically close this progress dialog';
dlgProgress1.Tag := 0;
dlgProgress1.Position := 0;
dlgProgress1.ShowCancel := True;
dlgProgress1.ShowModal;
CodeSite.Send('Progress dialog has been closed');
end;
procedure TForm1.dlgProgress1Progress(Sender: TObject; var AContinue: Boolean);
begin
if dlgProgress1.Tag = 0 then
begin
if dlgProgress1.Position < dlgProgress1.Max then
dlgProgress1.Position := dlgProgress1.Position + 1;
if dlgProgress1.Position = dlgProgress1.Max then
dlgProgress1.Tag := 1;
end
else
begin
if dlgProgress1.Position > 0 then
dlgProgress1.Position := dlgProgress1.Position - 1;
if dlgProgress1.Position = 0 then
dlgProgress1.Tag := 0;
end;
AContinue := not ShellExecAndWaitTaskTerminated;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(ShellExecAndWaitTask) then
ShellExecAndWaitTask.Cancel;
end;
When you close Notepad, shouldn't Assigned(ShellExecAndWaitTask) in the dlgProgress1Progress event handler become false and close the progress dialog by setting AContinue to false?
不,事情从来都不是这样的。例如,您可以有许多引用任务的不同变量。您怎么能期望所有这些变量都设置为 nil
.
不,接口引用变量保持赋值状态,直到它离开作用域,或者您将其设置为 nil
。顺便说一句,我严重质疑您为此使用全局变量的选择。我不知道你为什么做出那个决定,但这似乎是个错误的决定。
最简单的方法就是在调用 ShellExecAndWait
returns 时发送消息。
ShellExecAndWaitTask := TTask.Create(
procedure
begin
JclShell.ShellExecAndWait('notepad');
CodeSite.Send('Notepad has been closed');
// notify the main thread that the external process has completed
end);
例如,您可以使用 PostMessage
或 TThread.Queue
或 TThread.Synchronize
来通知主线程。