8

在给定的示例中,我在调用时收到异常AThread.Free.

program Project44;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, Windows;

type
  TMyException = class(Exception);

var
  AThread: TThread;
begin
  AThread := TThread.Create(True);
  try
    AThread.FreeOnTerminate := True;
    //I want to do some things here before starting the thread
    //During the setup phase some exception might occur, this exception is for simulating purpouses
      raise TMyException.Create('exception');
  except
    AThread.Free; //Another exception here
  end;
end.

我有两个问题:

  1. 在给定的示例中,我应该如何释放AThread实例TThread

  2. 我不明白,为什么在自我毁灭之前TThread.Destroy调用Resume。这有什么意义?

4

2 回答 2

15

您不能设置FreeOnTerminateTrue 调用线程Free实例。你必须做一个或另一个,但不能两者都做。就目前而言,您的代码会破坏线程两次。你绝不能两次销毁一个对象,当然,当析构函数第二次运行时,就会发生错误。

这里发生的情况是,由于您创建了挂起的线程,因此在您明确释放线程之前什么都不会发生。当你这样做时,析构函数恢复线程,等待它完成。这会导致Free再次调用,因为您设置FreeOnTerminateTrue. 第二次调用Free关闭句柄。然后您返回到线程 proc 并调用ExitThread. 这失败了,因为线程的句柄已关闭。

正如 Martin 在评论中指出的那样,您不能TThread直接创建,因为该 TThread.Execute方法是抽象的。此外,您不应使用Resume已弃用的。用于Start开始执行暂停的线程。

我个人不喜欢使用FreeOnTerminate. 使用此功能会导致线程在创建它的不同线程上被销毁。当您想忘记实例引用时,通常会使用它。这让您不确定当您的进程终止时线程是否已被销毁,或者甚至在进程终止期间它是否正在终止并释放自己。

如果您必须使用FreeOnTerminate,那么您需要确保Free在设置FreeOnTerminate为后不调用True。所以显而易见的解决方案是在调用后立即设置FreeOnTerminate为然后忘记线程实例。如果您在准备好开始之前有任何异常,那么您可以安全地释放线程,因为您仍然会在那个时候。TrueStartFreeOnTerminateFalse

Thread := TMyThread.Create(True);
Try
  //initialise thread object
Except
  Thread.Free;
  raise;
End;
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;

一种更优雅的方法是将所有初始化移动到TMyThread构造函数中。然后代码将如下所示:

Thread := TMyThread.Create(True);
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;
于 2012-01-10T15:44:47.020 回答
5

你的情况非常复杂。

首先,您实际上并没有释放挂起的线程;在析构函数中恢复线程:

  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;

由于Terminate被调用 before Resume,该Execute方法永远不会运行,并且线程在恢复后立即终止:

  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    Result := Thread.FReturnValue;
    FreeThread := Thread.FFreeOnTerminate;
    Thread.DoTerminate;
    Thread.FFinished := True;
    SignalSyncEvent;
    if FreeThread then Thread.Free;

现在看看最后一行——你Thread.Free从析构函数本身调用析构函数()!神奇的错误!


要回答您的问题:

  1. 你只是不能FreeOnTerminate:= True在你的代码中使用;
  2. 你应该问 Embarcadero 为什么 TThread 是这样设计的;我的猜测 - 一些代码(DoTerminate方法)应该在线程终止时在线程上下文中执行。

您可以向 QC 发送功能请求:添加FFreeOnTerminate:= FalseTThread.Destroy实现:

destructor TThread.Destroy;
begin
  FFreeOnTerminate:= False;
// everything else is the same
  ..
end;

这应该可以防止递归析构函数调用并使您的代码有效。

于 2012-01-10T16:31:40.557 回答