Delphi程序终止后的任务和线程问题

3

我有一个问题,无法在网上或我的 Delphi 书中解决。请看这个。

情况 1。

type
  Test = class(TThread)
    protected
      procedure Execute; override;
    public
      constructor Create;
  end;

constructor Test.Create;
begin
  inherited Create(false);
  FreeOnTerminate := true;
end;

procedure Test.Execute;
begin
  inherited;
  Sleep(5000);
  TFile.WriteAllText('C:\Users\EmmaVMWare\Desktop\ts.txt', 'test2');
end;

以下是 VCL 实现:

procedure TForm1.Button1Click(Sender: TObject);
var s: Test;
begin
  s := Test.Create;
  s := nil;
end;

我打开程序,点击按钮,等待5秒钟后在桌面上看到文件。但如果我打开程序,点击按钮后2秒钟关闭程序,那么在5秒钟后我就再也看不到文件了。
我读了Nick Hodges的《Delphi编程更多技巧》一书,他说这种线程是F&F("Fire and forget"),因为一旦启动,我们就让它自己运行并清理。 如果我在其终止之前关闭程序,它会被正确地关闭吗? 情况2。 出于好奇,我也写了这段代码:
procedure TForm1.Button1Click(Sender: TObject);
var s: ITask;
begin

  s := TTask.Run(procedure
                 begin
                  Sleep(5000);
                  TFile.WriteAllText('C:\Users\EmmaVMWare\Desktop\aa.txt', 'test');
                 end);
end;

这是 Case 1 的代码,但因为我使用了 Task,所以输入更少。在这种情况下情况并不相同!
如果我运行程序,点击按钮并等待 5 秒钟,我就可以看到文件。如果我运行程序,点击按钮,然后在 2 秒钟后关闭程序(或者在任何情况下,在 5 秒钟之前),我仍然可以看到文本文件!
Task:启动后会一直运行到结束,无论在终止之前是否关闭程序。
TThread 子类:启动后会一直运行到结束,但如果在其终止之前关闭程序,则会中断。
如上所述,我想知道在 Case 1 中,在线程终止之前只需关闭程序是否可行。因为我认为我可以这样写:
procedure TForm1.FormDestroy(Sender: TObject);
begin
   if not(s.Terminated) then
     s.Terminate;
end;

基本上,我不确定是否需要在代码中调用Terminate,或者当程序终止时它是否会自动调用。根据我的测试结果,这只影响TThread的子类,而不是任务(Tasks)。


如果你将FreeOnTerminate设置为True,那么你不能在一个线程上调用Terminate方法。如果在一个线程执行时终止进程,它将被强制终止。那么会发生什么呢?这取决于线程正在做什么。也许这并不重要,也许会有影响。 - David Heffernan
дҪҝз”ЁShutdownдәӢ件иҖҢдёҚжҳҜsleepпјҢжүҖжңүзәҝзЁӢдҪҝз”ЁWaitForSingleObject并зӯүеҫ…ShutdownдәӢ件гҖӮеҰӮжһңзӯүеҫ…и¶…ж—¶пјҢеҲҷ继з»ӯжү§иЎҢе…¶д»–ж“ҚдҪңпјҢеҗҰеҲҷз»ҲжӯўгҖӮ - FredS
1个回答

9

这与文件无关,而是Delphi运行时处理线程和任务的方式不同所导致的不幸差异。

当您退出VCL应用程序时,后台线程会被关闭。应用程序不会等待它们完成,而是直接结束。如果您想等待它们完成,您需要自己编写代码。

然而,一个VCL应用程序将会等待(如果必要的话,将会永远等待)所有放入默认线程池(System.Threading.TThreadPool.Default)中的任务。

考虑这个示例VCL应用程序:一个空窗体和两个按钮:

implementation uses System.Threading, Winapi.Windows;

procedure TForm1.Button1Click(Sender: TObject);
begin
    TThread.CreateAnonymousThread(backgroundStuff).Start();
    Application.Terminate();
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    TTask.Run(backgroundStuff);
    Application.Terminate();
end;

procedure TForm1.backgroundStuff();
begin
    Sleep(5000);
    Winapi.Windows.DebugBreak();
end;

在调试器中运行此应用程序。您会注意到,对于 Button1,您的应用程序将立即退出。对于 Button2 ,您的程序似乎已经关闭了 (窗体已关闭),但它仍在运行!最后,在五秒钟之后,您的调试器将在Winapi.Windows.DebugBreak()上中断。

奖励:TTask.Run(backgroundStuff);更改为TTask.Run(backgroundStuff, TThreadPool.Create());并查看发生了什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接