在给定的示例中,我在调用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.
我有两个问题:
>在给定的示例中,如何释放AThread的TThread实例?
>我不明白,为什么TThread.Destroy在销毁之前调用Resume.这有什么意义?
解决方法
您不能将FreeOnTerminate设置为True并在线程实例上调用Free.你必须做一个或另一个,但不是两个.因为它代表你的代码破坏线程两次.你必须永远不要销毁一个对象两次,当然,当析构函数第二次运行时,会发生错误.
这里发生的事情是,由于您创建了线程暂停,因此在您明确释放线程之前不会发生任何事情.当你这样做时,析构函数恢复线程,等待它完成.然后,这会导致Free被再次调用,因为您将FreeOnTerminate设置为True.第二次调用Free会关闭句柄.然后返回到线程proc并调用ExitThread
.这会失败,因为线程的句柄已经关闭.
正如Martin在评论中指出的那样,你不能直接创建TThread,因为TThread.Execute方法是抽象的.此外,您不应使用已弃用的Resume.使用“开始”开始执行挂起的线程.
我个人不喜欢使用FreeOnTerminate.使用此功能会导致线程在创建它的其他线程上被销毁.当您想要忘记实例引用时,通常会使用它.这使您不确定在进程终止时线程是否已被销毁,或者在进程终止期间是否终止并释放自身.
如果你必须使用FreeOnTerminate,那么你需要确保在将FreeOnTerminate设置为True后不要调用Free.因此,显而易见的解决方案是在调用Start之后立即将FreeOnTerminate设置为True,然后忘记线程实例.如果在准备好开始之前有任何例外,那么你可以放心地释放线程,因为FreeOnTerminate在那时仍然是False.
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;