销毁 TThread 对象的正确方法

发布于 2024-07-26 04:38:57 字数 1406 浏览 2 评论 0原文

这个问题看似微不足道,但我希望你不要忽视它。
在销毁 TThread 对象之前,通常需要等待调用 TThread.Execute() 方法的线程完成,因为只有这样我们才能确定,例如,在类的析构函数中销毁的对象不再被访问。 因此需要调用Terminate设置线程必须检查是否退出的Termerated标志,然后调用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)就足够了,而无需关心是否被销毁object 是否是 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 对象的方式(我希望是一种通用方式)的看法。 我问这个问题是因为我从同事的代码中了解到,他们通常使用第一个代码示例来销毁此类对象,但他们从未用于检查正在等待的线程是否没有被挂起,我认为如果线程有一点危险可能会在代码中的某个地方暂停。 因此,我试图找到一种通用的方法来销毁此类的对象,从而使代码更清晰、更安全。 我希望我没有让事情变得更糟——你觉得怎么样?

感谢您提前提出的建议。

This question may seem trivial, but I hope you won't ignore it.
Before destroying a TThread object it is usually necessary to wait until the thread that called the TThread.Execute() method finishes, for only then can we be sure that, for instance, the objects destroyed inside the class's destructor are no longer accessed. Therefore it is necessary to call Terminate to set the Terminated flag that the thread has to check to know whether to exit or not, and then call the WaitFor() method.

Because the thread may be suspended, I think it is good to resume it before calling WaitFor, as otherwise the calling thread would be deadlocked. And because the thread can be suspended multiple times, it should be resumed the same number of times, right?

while Suspended do
  Resume;

If the thread was created suspended, we do not have to worry that the TThread.Execute() method will be called when we resume the thread only to terminate it - it won't (please correct me if I'm wrong).

What I've stated suggests using the following lines of code for each TThread object being freed:

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

Unfortunately, when we destroy our application that has created multiple threads, writing such a piece of code for each TThread object being destroyed unnecessarily makes the code very long and maybe even opaque.

Therefore I came to a conclusion that all these could be put inside an overriden destructor of the TThread class thanks to which it would be enough to call MyThread.Free (or MyThread.Terminate if MyThread.FreeOnTerminate is set) without caring about whether the destroyed object is a TThread object or not:

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;

Forgive me asking such a basic question. I would like, however, to get to know your opinions about this way - I hope an universal way - of destroying TThread objects. I ask this questions for I learned from my workmates' codes that they usually used the first example of code to destroy such objects, but they never used to check whether the threads being waited for were not suspended which I considered a bit dangerous if the threads might be suspended somewhere in the code. Therefore I tried to find a universal way of destroying the objects of this class that would make the code clearer and safer. I hope I didn't make it worse - what do you think?

Thanks for your suggestions in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

岁月蹉跎了容颜 2024-08-02 04:38:57

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

Much of what your suggesting is already performed in the TThread.Destroy destructor, and invoking TMyThread.free will do just what your suggesting. To cleanup any objects owned by the thread class, you can perform that in the OnTerminate event, which will get invoked as part of the thread shutdown logic.

冰雪之触 2024-08-02 04:38:57

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

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

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

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

某些线程设置了其 FreeOnTerminate 属性。 对于这些线程,您的代码不安全。 从技术上讲,对此类对象调用任何方法都是不安全的,因为线程可能随时终止。 但即使您知道线程仍在运行并且线程对象仍然存在,该对象也肯定会在调用 Terminate 后的某个时间停止存在。 您不能在 free-on-terminate 线程对象上调用 WaitFor,也绝对不能调用 Free

There is no universal way to stop a thread, just as there is no universal way to (gracefully) stop a process. Each one is different.

For some threads, it's sufficient to set its Terminated property via the Terminate method. Other threads, however, call functions like GetMessage or MsgWaitForMultipleObjects, which will block until something happens, such as a message arriving or a kernel handle becoming signaled. TThread.Terminate can't make either of those things happen, so it can't make those threads stop running. When I've written threads like those, I've provided my own functions for notifying them to stop running. I might call PostThreadMessage to force a message onto the thread's queue, or I might signal the event that the thread class has provided for notifying it of a request to terminate.

Don't worry about resuming a suspended thread. You really shouldn't be suspending them anyway. The only safe way to suspend a thread is for the thread to suspend itself, and once you have that, you're guaranteed to have at least two threads controlling when the thread runs: the thread itself to suspend it, and at least one other thread to resume it again. A thread should be in control of its own execution.

It would be great if TThread.Terminate were virtual. Then each thread class could provide a custom way to notify itself that it should stop running. Some could just set Terminated, and others could post themselves messages, signal events, or do whatever else they need. As it is, though, the non-virtual method doesn't work well with threads that spend a lot of their time waiting for other things. The current way only works for threads that are able to frequently poll their Terminated properties.

Some threads have their FreeOnTerminate properties set. For those threads, your code is not safe. Technically, it's not safe to call any methods on such objects since the thread could terminate at any time. But even if you know the thread is still running and the thread object still exists, the object will definitely stop existing sometime after the call to Terminate. You can't call WaitFor on a free-on-terminate thread object, and you definitely can't call Free.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文