线程错误:当尝试释放挂起的线程时,句柄无效(6)

Wod*_*dzu 8 delphi exception tthread delphi-2009

在给定的示例中,我在调用时收到异常 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.
Run Code Online (Sandbox Code Playgroud)

我有两个问题:

  1. 我应该如何释放AThread的实例TThread在给定的例子吗?

  2. 我不明白,为什么TThread.Destroy呼吁Resumedestroing本身之前.这有什么意义?

Dav*_*nan 15

您不能设置FreeOnTerminateTrue 调用Free的线程实例.你必须做一个或另一个,但不是两个.因为它代表你的代码破坏线程两次.你必须永远不要销毁一个对象两次,当然,当析构函数第二次运行时,会发生错误.

这里发生的是,因为你创建了线程暂停,所以在你明确释放线程之前没有任何反应.当你这样做时,析构函数恢复线程,等待它完成.然后,这会导致Free再次调用,因为您设置FreeOnTerminateTrue.第二次调用Free关闭句柄.然后你返回线程proc和那个调用ExitThread.这会失败,因为线程的句柄已经关闭.

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

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

如果必须使用FreeOnTerminate,那么你需要确保你不叫Free已经设置后FreeOnTerminateTrue.所以显而易见的解决方案是设置FreeOnTerminateTrue打完电话后,立即之前Start,然后忘掉线程实例.如果在准备好开始之前有任何例外,那么你可以放心地释放线程,因为你FreeOnTerminate仍然会False在那一点上.

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

更优雅的方法是将所有初始化移动到TMyThread构造函数中.然后代码看起来像这样:

Thread := TMyThread.Create(True);
Thread.FreeOnTerminate := True;
Thread.Start;
Thread := nil;
Run Code Online (Sandbox Code Playgroud)

  • @Sertac是的,这也可以,但我认为在线程构造函数中移动代码确实是最好的方法. (3认同)

klu*_*udg 5

在你的情况下,情况非常复杂.

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

  begin
    Terminate;
    if FCreateSuspended then
      Resume;
    WaitFor;
  end;
Run Code Online (Sandbox Code Playgroud)

由于Terminate之前调用过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;
Run Code Online (Sandbox Code Playgroud)

现在看看最后一行 - 你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;
Run Code Online (Sandbox Code Playgroud)

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