这个问题看似微不足道,但我希望你不要忽视它。
在销毁 TThread 对象之前,通常需要等待调用 TThread.Execute() 方法的线程完成,因为只有这样我们才能确定,例如,在类的析构函数中销毁的对象不再被访问。因此需要调用Terminate设置线程必须检查的终止标志以了解是否退出,然后调用WaitFor()方法。

因为线程可能会被挂起,所以我认为最好在调用 WaitFor 之前恢复它,否则调用线程会死锁。而且因为线程可以被挂起多次,所以它应该恢复相同的次数,对吗?

while Suspended do
  Resume;

如果线程是挂起创建的,我们不必担心当我们恢复线程只是为了终止它时会调用 TThread.Execute() 方法 - 它不会(如果我错了,请纠正我)。

我所说的建议对每个要释放的 TThread 对象使用以下代码行:

MyThread.Terminate;
while MyThread.Suspended do
  MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;

不幸的是,当我们销毁创建了多个线程的应用程序时,为每个被销毁的 TThread 对象编写这样一段代码是不必要的,这会使代码变得非常长,甚至可能不透明。

因此,我得出的结论是,所有这些都可以放入 TThread 类的重写析构函数中,因此调用 MyThread.Free(或 MyThread.Terminate,如果设置了 MyThread.FreeOnTerminate)就足够了,而无需关心是否被销毁对象是否是 TThread 对象:

destructor TMyThread.Destroy;
begin
  //if FreeOnTerminate, the calling thread cannot wait for itself
  if GetCurrentThreadId <> ThreadId then
  begin
    Terminate;
    while Suspended do
      Resume;
    WaitFor;
  end;

  {free all objects created in this class}

  inherited Destroy;
end;

请原谅我问这么基本的问题。然而,我想了解您对这种销毁 TThread 对象的方式(我希望是一种通用方式)的看法。我问这个问题是因为我从同事的代码中了解到,他们通常使用第一个代码示例来销毁此类对象,但他们从未用于检查正在等待的线程是否没有被挂起,我认为如果线程有一点危险可能会在代码中的某个地方暂停。因此,我试图找到一种通用的方法来销毁此类的对象,从而使代码更清晰、更安全。我希望我没有让事情变得更糟——你觉得怎么样?

感谢您提前提出的建议。

有帮助吗?

解决方案

您的建议的大部分内容已经在 TThread.Destroy 析构函数中执行,并且调用 TMyThread.free 将执行您的建议。要清理线程类拥有的任何对象,您可以在 OnTerminate 事件中执行该操作,该事件将作为线程关闭逻辑的一部分被调用。

其他提示

没有通用的方法来停止线程,就像没有通用的方法来(优雅地)停止进程一样。每一个都是不同的。

对于某些线程,设置它的值就足够了 Terminated 财产通过 Terminate 方法。然而,其他线程调用类似的函数 GetMessage 或者 MsgWaitForMultipleObjects, ,它将阻塞,直到发生某些事情,例如消息到达或内核句柄收到信号。 TThread.Terminate 无法使这些事情发生,因此它无法使这些线程停止运行。当我编写这样的线程时,我提供了自己的函数来通知它们停止运行。我可能会打电话 PostThreadMessage 强制将消息放入线程队列,或者我可能会发出线程类提供的事件信号,以通知其终止请求。

不用担心恢复挂起的线程。无论如何,你真的不应该暂停它们。挂起线程的唯一安全方法是线程挂起自身,一旦实现,就保证至少有两个线程控制线程运行:线程本身将其挂起,并且至少有一个其他线程将其再次恢复。线程应该控制自己的执行。

如果 TThread.Terminate 是虚拟的。然后每个线程类可以提供一种自定义方法来通知自己应该停止运行。有些可以直接设置 Terminated, ,其他人可以发布自己的消息、发出事件信号或做任何他们需要的事情。但事实上,非虚拟方法对于花费大量时间等待其他事情的线程来说效果不佳。当前的方式仅适用于能够频繁执行的线程 轮询 他们的 Terminated 特性。

有些线程有它们的 FreeOnTerminate 属性设置。对于这些线程,您的代码不安全。从技术上讲,打电话并不安全 任何 此类对象上的方法,因为线程可以随时终止。但即使你知道线程仍在运行并且线程对象仍然存在,该对象肯定会在调用后的某个时间停止存在 Terminate. 。你不能打电话 WaitFor 在终止时释放的线程对象上,你绝对不能调用 Free.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top