【问题标题】:What's the correct way in Delphi to check if a non-Suspended created TThread with FreeOnTerminate = True is still executing?Delphi中检查FreeOnTerminate = True的非暂停创建的TThread是否仍在执行的正确方法是什么?
【发布时间】:2022-02-03 18:12:00
【问题描述】:

所以我创建了一个类:TWorkerThread = class(TThread)。在TWorkerThread 构造函数中,我设置了FreeOnTerminate := True

然后我向我的TForm 添加了一个私有Worker 变量。当我为Worker 实例化TWorkerThread 时,我总是使用CreateSuspended := False

一旦启动,我需要与线程进行的唯一交互是取消进程 (Terminate) 并检查进程是否结束(该检查基于用户事件)。

要检查线程是否仍处于活动状态,我会这样做:

if (Self.Worker <> nil) and (not Self.Worker.Finished) then
    //Still active

这似乎工作正常,但我担心在某些时候TWorkerThread 将是&lt;&gt; nil 但仍然是.free(请记住TWorkerThread 都是FreeOnTerminate := True)。当TWorkerThread“自我毁灭”一旦终止,他们总是变成nil吗?如果没有,我该如何处理(以避免访问冲突)?

另一件事,如果Worker 在它的表单销毁上处于活动状态,我会这样做:

if (Self.Worker <> nil) and (not Self.Worker.Finished) then
begin
    Self.Worker.Terminate;
    //wait here for completion
end;

Terminate 之后,如果我执行WaitFor,我会遇到访问冲突(因为自毁)。我应该在这里强制等待吗?最好的方法是什么?

这是我在 Delphi 中第一次正确使用 TThread。上面的代码是我为清楚起见所做的简化版本。

【问题讨论】:

  • 一旦你在终止线程上启动了一个空闲线程,由于竞争条件,你不能调用任何方法或从线程外部引用任何字段。相反,您需要让线程在终止时通知您。
  • 如果您需要检查线程状态或者您需要终止线程或任何类似的能力,您不能使用自毁线程。没有其他可行的选择。
  • @DavidHeffernan 这并不完全正确。只要 1. 线程有一个 OnTerminate 事件处理程序,并且 2. OnTerminate 事件处理程序没有对线程的引用,您仍然可以使用来自主线程的线程对象。 OnTerminate 事件总是在主线程的上下文中执行,所以只要线程引用不为 nil,就可以安全地使用线程引用。虽然需要小心调用线程的哪些函数以避免死锁。
  • @KenBourassa 你所说的不会改变大卫所说的。 “您需要让线程在终止时通知您...使用OnTerminate 来通知” 但是请注意,线程可以覆盖@ 987654343@ 触发OnTerminate 没有 使用TThread.Synchronize() (我有线程可以做到这一点),因此重新引入线程引用的竞争条件,如果你说不可能发生你使用OnTerminate。但这是一种更高级的用法,大多数人大部分时间都不太可能这样做,在这种情况下,你说的就行了。
  • @RemyLebeau 我从未质疑过大卫的那部分观点。我质疑如果说线程是 FreeOnTerminate 则不能使用线程引用的概念,这是错误的。至于你的第二点,是的,我想你可以覆盖TThread.DoTerminate() 来调用OnTerminate 而没有TThread.Synchronize()。考虑到TThread.OnTerminate 被明确记录为在主线程的上下文中执行,我们不再谈论 TThread,我们谈论的是TYourThread。但由于它违反了 TThread 引入的“合同”,我将为此引入一个新事件。

标签: multithreading delphi delphi-10.4-sydney


【解决方案1】:

Delphi 中检查非暂停创建的正确方法是什么 FreeOnTerminate = True 的 TThread 仍在执行?

没有。

一旦启动自毁线程,就不再允许从外部代码中触摸持有此类线程的引用,因为该引用随时可能变得陈旧。

在某些情况下(如果在您的自定义线程类中未修改DoTerminate 中的逻辑处理OnTerminate 调用),您可以安全地从OnTerminate 事件处理程序访问线程引用。但是,这不会帮助您解决问题,因为您不能等待自毁线程。在 Windows 上,此类代码会导致可以捕获和处理的异常,但在其他平台上,它会导致未定义的行为和随机死锁。但即使在 Windows 上编写这样的代码也是不可取的。

如果你需要检查一个线程的状态,取消它或等待它,你不能使用自毁线程。时期。 (对于那些可能反对之前陈述的人,是的,在某些情况下,其中一些事情是可能的,但它们通常会导致自己的脚被射中。

您可以将以下工作流与TWorkerThread 一起使用,前提是您将FreeOnTerminate 留在线程构造函数中的False 上并构造未挂起的线程。必须从主线程调用以下所有方法以避免Worker 引用上的竞争条件。在您的表单打开时拥有一个不执行任何操作的额外线程不会给您带来任何麻烦。

procedure TMyForm.StartWorker;
begin
  FreeAndNil(Worker); // or if Assigned(Worker) then Exit;
  Worker := TWorkerThread.Create;
end;

procedure TMyForm.CancelWorker;
begin
  // this will initiate thread termination and waiting
  FreeAndNil(Worker);
end;

procedure TMyForm.Destroy;
begin
  // technically we could only use Free here, 
  // but since other code requires niling the reference this is more consistent 
  FreeAndNil(Worker);
  inherited;
end;

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
  • 2019-09-16
  • 1970-01-01
  • 2015-02-21
  • 1970-01-01
相关资源
最近更新 更多