delphi 我可以从VCL线程调用TerminateThread吗?

ao218c7q  于 2022-12-18  发布在  其他
关注(0)|答案(2)|浏览(156)

我发现了一个叫做TBackgroundWorker的this nice component。但是,人们批评它(在SO上),因为它使用了TerminateThread。下面是“错误的”代码:

destructor TBackgroundWorker.Destroy;
begin
  if IsWorking then
  begin
    TerminateThread(fThread.Handle, 0);
    Cleanup(True);
    raise EBackgroundWorker.CreateFmt(SInvalidExit, [Name]);
  end;
  inherited Destroy;
end;

对我来说它似乎是一个有效的析构函数。是吗?我应该担心吗?
有更好的解决办法吗?

yruzcnhs

yruzcnhs1#

在我看来,析构函数是有效的。
强制终止线程是错误的。另外,在析构函数中引发异常可能会杀死整个应用程序。但是,请不要忽略上下文。
我们讨论了一个代理对象,它 Package 了一个线程。如果这样的组件正在运行,那么它的破坏就相当于杀死一个正在运行的线程。代理应该快速失效并报告这样的错误行为,而不是操纵它。此外,这是一个第三方组件,它不知道应用程序开发者的意图。
我想你不同意我的意见否则,我们就不会有这次谈话。让我们看看还有什么选择。
1.**取消任务并优雅地终止线程,没有异常消息。**使用这种方法,我们是在猜测开发人员的意图。如果开发人员犯了错误,他或她可能永远不会知道,直到为时已晚。应用程序将有意想不到的行为,并且找出问题的根源非常复杂。
1.**忽略正在运行的线程并销毁组件,而不引发异常。**看起来像是将确定性机器变成了非确定性机器。我们甚至需要讨论这个问题吗?
1.**只是引发一个异常。**因为线程仍在运行,变量和堆栈跟踪可能包含误导性的状态,这使得调试更加困难。
我相信我们都喜欢在开发的早期阶段就发现错误,并为客户提供可靠和稳定的应用程序。我们是否应该停止这样做,因为我们需要使用的工具没有有效的用例?
总有一个有效的用例。如果我错了,请告诉我。

wj8zmpe1

wj8zmpe12#

对我来说它似乎是一个有效的析构函数。是吗?我应该担心吗?
这是一个错误的析构函数代码。
首先,你听说过的关于TerminateThread的所有不好的事情都是真的。没有安全的方法来终止线程,因为它可能会使应用程序处于不稳定的状态,你永远不应该使用这个函数,除非你也想立即关闭应用程序。在这种情况下,最好是完全退出进程。参见:Calling TerminateThread on a Windows thread when app exits
Windows started picking up the really big pieces of TerminateThread garbage on the sidewalk, but it’s still garbage on the sidewalk
现在说说历史。
最初,Terminate Thread函数并不存在。最初的设计者强烈地认为不应该存在这样的函数,因为没有安全的方法来终止线程,而且拥有一个不能安全调用的函数也没有意义。但是人们强烈要求他们需要Terminate Thread函数,尽管它并不安全。所以操作系统的设计者屈服了,因为人们需要它,所以添加了这个函数。当然,那些坚持他们需要终止线程的人现在后悔已经给了它。
这是那种“小心你的愿望”的事情。
此外,析构函数会引发异常,这是 Delphi 析构函数永远不应该做的事情。在析构函数中引发异常(不在try..except块中捕获和处理)将导致应用程序中不可修复的内存泄漏。
有更好的解决办法吗?
是。由于Cleanup方法将调用fThread.FreefThread.Free将等待线程完成并执行正常线程关闭,因此无需调用TerminateThread
与其强制线程终止,不如对线程执行Cancel操作,并给予它时间让它自行终止。这也可能需要调用WaitFor,尽管此时发送Windows消息可能会干扰其他应用程序代码。

destructor TBackgroundWorker.Destroy;
begin
  if IsWorking then
    begin
      Cancel;
      // WaitFor; 
      Cleanup(True);
    end;
  inherited Destroy;
end;

最后,如果线程在关闭期间仍在运行,则处理所发生的情况不在组件的域中。如果需要处理这种情况并防止关闭,则需要从外部代码进行处理。
一般来说,我会避免使用这个组件,因为一般化的解决方案可能会产生比其价值更多的问题。通过泵送消息来等待线程并不是最好的设计。它可能在某些情况下工作得很好,而在其他情况下则不行。
依赖于TThread.WaitFor函数可能是更好的体系结构,但是TThread.WaitFor正在阻塞调用,因此该行为可能不适合TBackgroundWorker体系结构和所需行为。

**注意:**我没有完全检查TBackgroundWorker组件的代码,因此可能存在本文未涉及的其他问题。

相关问题