使用 TThread.Resume 有什么问题?

What's wrong with using TThread.Resume?

很久以前,当我开始在 Delphi 中使用线程时,我通过在其构造函数的末尾调用 TThread.Resume 让线程自行启动,现在仍然如此:

constructor TMyThread.Create(const ASomeParam: String);
begin
  inherited Create(True);
  try
    FSomeParam:= ASomeParam;
    //Initialize some stuff here...
  finally
    Resume;
  end;
end;

从那时起,Resume 已被弃用,取而代之的是使用 Start。但是,Start只能在线程外调用,不能在构造函数内调用。

我继续使用 Resume 设计我的线程,如上所示,尽管我知道它已被弃用 - 只是因为我不想从线程外部调用 Start。我觉得必须打电话有点麻烦:

FMyThread := TMyThread.Create(SomeParamValue);
FMyThread.Start;

问:做这个改动的原因是什么?我的意思是,使用 Resume 有什么不对,他们希望我们使用 Start 来代替?

EDIT 在 Sedat 的回答之后,我想这真的取决于在构造函数中线程何时真正开始执行。

简短而精辟的答案是因为 TThread class 的作者不相信开发人员会阅读或理解文档。 :)

挂起和恢复线程是合法操作,仅适用于非常 数量有限的用例。事实上,这个有限的数量本质上是 "one": Debuggers

不受欢迎

它被认为是不受欢迎的(至少可以说)的原因是,如果一个线程被挂起,而(例如)它拥有对其他同步对象(如互斥体或信号量等)的锁,则会出现问题。

这些同步对象专门用于确保线程相对于访问共享资源的其他线程的安全操作,因此中断和干扰这些机制很可能会导致问题。

调试器需要一种工具来[=44​​=]直接挂起线程,而不管这些机制为何,原因非常相似。

例如,考虑断点涉及线程上的隐式(或者您甚至可以说 explicit)"suspend" 操作。如果调试器在到达断点时暂停线程,那么它也必须准确地暂停进程中的所有其他线程,否则它们将抢先执行可能会干扰调试器可能被要求执行的许多低级任务的工作然后做。

调试器的强壮手臂

调试器不能 "inject" 漂亮、有礼貌的同步对象和机制来请求这些其他线程以协调的方式将自己挂起,而其他一些线程已经被粗暴地停止(通过断点)。调试器别无选择,只能加强线程,而这正是 Suspend/Resume API 的目的。

它们适用于您需要停止线程的情况“现在。无论您在做什么我都不在乎,只需停止!”。稍后,然后说“好的,你现在可以继续你之前在做什么,不管它是什么。”。

表现良好的线程彼此表现良好

很明显,这不是一个行为良好的线程在正常操作中如何与其他线程交互(如果它希望保持 "normal" 操作而不会产生各种问题)。在那些正常情况下,一个线程非常确实并且应该关心那些其他线程在做什么并确保它不会干扰,使用适当的同步与其他线程协调的技术。

在那些情况下,恢复线程的合法用例类似地减少到只有一个单一模式。也就是说,您已经创建并初始化了一个线程,您不希望立即 运行 而是 start 在某个其他线程的控制下的某个时间点执行.

但是一旦新线程 已经 启动,随后与其他线程的同步 必须 使用那些适当的同步技术来实现,不是暂停它的蛮力。

开始 vs Suspend/Resume

因此决定 Suspend/Resume 在通用线程 class 上没有真正的位置(人们实施调试器仍然可以直接调用 Windows API),而是提供了更合适的 "Start" 机制。

希望大家清楚,即使此 Start 机制使用的 API 与之前使用的已弃用的 Resume 方法完全相同,但目的却大不相同。