【问题标题】:When does background threads prevent the process of being terminated?后台线程何时阻止进程被终止?
【发布时间】:2014-07-12 19:57:01
【问题描述】:

我们的程序在程序开始时创建了一个后台线程。后台线程使用 Indy 进行一些数据库完整性检查并检查 Internet 中的内容。 10 秒后,后台线程应该结束了,因为 FreeOnTerminate 为真,它也会自行清理。

我们注意到,在某些情况下,如果用户关闭程序过快,该进程在后台线程结束之前仍然处于活动状态。

由于我们无法准确重现该问题,因此我创建了一个演示项目来尝试一些事情:

type
  TBackgroundThread = class(TThread)
  protected
    procedure Execute; override;
  end;

{ TForm1 }

var
  bt: TBackgroundThread;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
begin
  // Create a background thread which runs X seconds and then terminates itself.
  bt := TBackgroundThread.Create(false);
  bt.FreeOnTerminate := true;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  // The user closes the app while the background thread is still active
  Sleep(2000);
  Close;
end;

{ TBackgroundThread }

procedure TBackgroundThread.Execute;
var
  i: integer;
  x: cardinal;
begin
  inherited;

  // Simulate some work that the background thread does
  x := MaxInt;
  for i := 0 to MaxInt do
  begin
    x := Random(x);
  end;
end;

结果让我有点意外:关闭MainForm后,进程立即终止,后台线程被硬杀。

现在我有几个问题:

  1. 在 MainForm 关闭(= 主线程退出)后,我应该通过 .Terminate 手动终止所有创建的线程还是自动终止?

  2. 我的线程应该只检查 Self.Terminated 还是应该同时检查 Application.Terminated ?

  3. 为什么当我关闭应用程序时,如上所示的繁忙线程会立即被终止?我预计 Project1.exe 进程将一直运行,直到所有线程都自行完成。 (如上所述,我们看到了一个应用程序,其中主窗体已关闭,但一个线程正在阻止进程被关闭。

  4. 那么,我们真正的应用程序的进程怎么可能不会因为后台线程运行而终止呢?可能与互联网有关,这可能导致应用等待连接超时?

【问题讨论】:

  • 嗨。这是很多问题,但我们都去过那里。我建议您阅读 Joe Duffy 的书“Windows 上的并发编程”以了解基本概念(虽然不是那么基本),然后阅读 Delphi 文档。
  • 我认为 Indy 正在阻止您的线程立即完成。我猜你可以使用超时阻塞套接字。
  • 但是@Lurd,阻塞套接字是否会阻止ExitProcess 杀死线程?我对此表示怀疑。
  • @RobKennedy 好吧,有一些版本的 Indy 带有可怕的析构函数,如果被调用,它们会等到所有 TidThread 等实例都被终止。由于这些原因,我避免将 Indy 组件添加到表单和数据模块上,而只是在需要使用它们的任何线程中创建它们。如果狡猾的析构函数永远不会被调用,它们就无法通过阻止到达 ExitProcess() 来阻止应用程序关闭进程。
  • 一般来说 - 不要将线程子系统组件插入表单。如果这样做,在应用程序关闭时释放拥有的表单将尝试释放组件,如果内部线程,可能会卡住等待线程终止。

标签: multithreading delphi indy terminate


【解决方案1】:

关闭主窗体并不等同于退出主线程。表单关闭后代码继续运行。尤其是单元是最终确定的。

如果您处理测试线程的OnTerminate 事件,或在Terminate 方法中放置断点,您会看到程序退出时它不会自动调用。你必须自己调用它。但还要注意,线程不会因为调用Terminate 而停止运行。它继续运行,直到它自己停止或被强制终止。调用WaitFor 等待它终止。

不要费心检查Application.Terminated;线程的属性应该足够了。

当您的程序退出时,您的线程会被强制终止,因为最终您的程序会调用ExitProcess,而操作系统所做的事情之一就是终止所有其他线程。它不会对它们调用Terminate,因为操作系统不知道 Delphi 类和方法。

您必须进行更多调试才能确定您的程序没有为您的客户立即终止的原因。您说您无法在内部重现该问题,并且您编写的测试程序也没有出现该问题。您必须找到一个愿意配合您进一步调试工作的客户。你真的知道是线程在阻止事情吗,或者这只是目前的猜测?

【讨论】:

  • 在设置了FreeOnTerminate 的线程上调用Waitfor 是不安全的。见A proper way of destroying a TThread object
  • 谢谢。现在,许多事情变得更加清晰。我重构了大部分代码,不再使用FreeOnTerminate。相反,我将所有创建的线程写入一个 ObjectList,每个元素都将在程序终止(单元的最终确定)时使用Terminate+WaitFor+Free 处理。在每个线程中,我都会定期检查Self.Terminated
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多