首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自动或手动释放TThread

自动或手动释放TThread
EN

Stack Overflow用户
提问于 2010-08-24 21:47:13
回答 4查看 5.7K关注 0票数 9

我在我的程序中有一个主线程和一个单独的线程。如果独立线程在主线程之前完成,它应该自动释放自己。如果主线程首先完成,它应该释放单独的线程。

我知道FreeOnTerminate,而且我读到过你必须小心使用它。

我的问题是,下面的代码正确吗?

代码语言:javascript
复制
procedure TMyThread.Execute;
begin
  ... Do some processing

  Synchronize(ThreadFinished);

  if Terminated then exit;

  FreeOnTerminate := true;
end;

procedure TMyThread.ThreadFinished;
begin
  MainForm.MyThreadReady := true;
end;

procedure TMainForm.Create;
begin
  MyThreadReady := false;

  MyThread := TMyThread.Create(false);
end;

procedure TMainForm.Close;
begin
  if not MyThreadReady then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;
end;
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-08-24 22:34:43

您可以将其简化为:

代码语言:javascript
复制
procedure TMyThread.Execute;
begin
  // ... Do some processing
end;

procedure TMainForm.Create;
begin
  MyThread := TMyThread.Create(false);
end;

procedure TMainForm.Close;
begin
  if Assigned(MyThread) then
    MyThread.Terminate;
  MyThread.Free;
end;

解释:

  • 要么使用FreeOnTerminate,要么手动释放线程,但决不能同时使用这两种方法。线程执行的异步本质意味着您可能不会释放线程,或者(更糟糕的是)执行两次。在线程对象完成执行后将其保留不会有任何风险,在已经完成的线程上调用Terminate()也不会有风险。
  • 不需要同步对仅从一个线程写入而从另一个线程读取的布尔值的访问。在最坏的情况下,您会得到错误的值,但由于异步执行,这无论如何都是一个虚假的效果。只有不能以原子方式读取或写入的数据才需要同步。如果你需要同步,不要使用Synchronize()
  • 没有必要使用类似MyThreadReady的变量,因为你可以使用WaitForSingleObject()查询线程的状态。将MyThread.Handle作为第一个参数传递给它,将0作为第二个参数传递给它,并检查结果是否为WAIT_OBJECT_0 -如果是的话,您的线程已经完成了执行。

顺便说一句:不要使用OnClose事件,而要使用OnDestroy。前者不一定会被调用,在这种情况下,您的线程可能会继续运行并使您的进程保持活动状态。

票数 8
EN

Stack Overflow用户

发布于 2010-08-25 01:06:03

让主线程为辅助线程的OnTerminate事件分配一个处理程序。如果工作线程首先完成,则处理程序可以向主线程发出释放线程的信号。如果主线程首先完成,它可以终止辅助线程。例如:

代码语言:javascript
复制
procedure TMyThread.Execute;
begin
  ... Do some processing ...
end;

procedure TMainForm.Create;
begin
  MyThread := TMyThread.Create(True);
  MyThread.OnTerminate := ThreadFinished;
  MyThread.Resume; // or MyThread.Start; in D2010+
end;

const
  APPWM_FREE_THREAD = WM_APP+1;

procedure TMainForm.ThreadFinished(Sender: TObject);
begin
  PostMessage(Handle, APPWM_FREE_THREAD, 0, 0);
end;

procedure TMainForm.WndProc(var Message: TMessage);
begin
  if Message.Msg = APPWM_FREE_THREAD then
    StopWorkerThread
  else
    inherited;
end;

procedure TMainForm.StopWorkerThread;
begin
  if MyThread <> nil then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    FreeAndNil(MyThread);
  end;
end;

procedure TMainForm.Close;
begin
  StopWorkerThread;
end;
票数 4
EN

Stack Overflow用户

发布于 2010-08-24 22:17:19

不,你的代码不好(尽管它可能在99.99%甚至100%的情况下都能工作)。如果您计划从主线程终止工作线程,不要将FreeOnTerminate设置为True (我看不出您试图通过将FreeOnTerminate设置为True在上面的代码中获得什么,这至少会使您的代码更难理解)。

终止工作线程的一个更重要的情况是,当工作线程处于等待状态时,您正在尝试关闭应用程序。如果你只是调用Terminate,线程将不会被唤醒,通常你应该使用额外的同步对象(通常是事件)来唤醒工作线程。

再说一句--没有必要

代码语言:javascript
复制
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;

如果您查看TThread.Destroy代码,它将调用Terminate和WaitFor,因此

代码语言:javascript
复制
    MyThread.Free;

足够了(至少在Delphi 2009中,手头没有Delphi 7源码可供检查)。

已更新

阅读mghie答案。考虑以下情况(在1个CPU系统上更好):

主线程正在执行

代码语言:javascript
复制
procedure TMainForm.Close;
begin
  if not MyThreadReady then
  begin
    MyThread.Terminate;
    MyThread.WaitFor;
    MyThread.Free;
  end;
end;

它检查了MyThreadReady值(它是False),并被调度程序关闭。

现在调度器切换到工作线程;它执行

代码语言:javascript
复制
  Synchronize(ThreadFinished);

并强制调度器切换回主线程。主线程继续执行:

代码语言:javascript
复制
    MyThread.Terminate;   // no problem
    MyThread.WaitFor;     // ???
    MyThread.Free;

你能说一下WaitFor会发生什么吗?我不能(需要更深入地研究TThread源代码才能回答,但乍看起来像是死锁)。

你真正的错误是不同的东西-你写了一个不可靠的代码,并试图找出它是正确的还是错误的。这是一个糟糕的线程实践--你应该学会写一个可靠的代码。

至于资源-当TThread ( FreeOnTerminate = False)被终止时,剩下的资源只有Windows线程句柄(线程终止后它不会使用大量的Windows资源)和内存中的Delphi TThread对象。为了安全起见,这并不是很大的成本。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3557105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档