如何使用Thread.terminate命令避免崩溃?

时间:2016-11-29 19:00:58

标签: delphi

场景1)当我点击button1然后在线程正常工作时用X关闭表单时,我得到"线程错误:句柄无效"

场景2)当我在没有点击button1的情况下关闭应用程序时,我得到了#34;访问违规..."

    if (!strcmp(myName, "Program1"))
      printf("I am program 1!");
    else
      printf("I am someone else !");

2 个答案:

答案 0 :(得分:5)

FreeOnTerminate = true时不要引用线程对象。该线程可能已经完成其工作并自行销毁,因此访问它可能不安全。

OnCloseQuery事件处理程序中,如果未单击Button1,您还会访问未初始化的对象。

如果要控制线程的生命周期,请保留FreeOnTerminate = false

OnCloseQuery事件处理程序中,检查是否在终止线程之前分配了线程,并防止Button1 click事件一次启动多个线程。

TMyThread0.Execute()中,访问类的字段和方法时,不得引用特定的线程实例。写下这个:

until Terminated;  

答案 1 :(得分:3)

请勿将TThread.WaitFor()TThread.FreeOnTerminate=True一起使用。

Execute()退出时,如果TThread.FreeOnTerminate=True,则TThread对象会自行销毁,关闭TThread.WaitFor()等待的线程句柄。所以你可能会看到"无效句柄"错误。或者您可能会遇到访问冲突或任何其他意外错误/症状,因为由于竞争条件可能会在无效对象上调用WaitFor()而导致未定义行为,或者通常在WaitFor()仍在运行时对象被销毁。并且WaitFor()在任何操作系统错误上引发异常,包括"无效句柄"错误。

设置TThread.FreeOnTerminate=True主要用于一旦启动就被遗忘的线程。如果您需要在线程启动后引用它,请不要使用FreeOnTerminate。你不希望线程在你背后消失。

此外,Execute()不应通过外部对象指针访问其Terminated属性。改为使用Self指针。

请改为尝试:

procedure TForm1.Button1Click(Sender: TObject);  
begin
  ProccesSupervisor := TMyThread0.Create(False);
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if ProccesSupervisor <> nil then
  begin
    ProccesSupervisor.Terminate;
    ProccesSupervisor.WaitFor;
    FreeAndnil(ProccesSupervisor);
  end;
end;

procedure TMyThread0.Execute;
begin
  while not Terminated do
  begin
    //some code here
  end;
end;

如果您绝对必须设置TThread.FreeOnTerminate=True,那么您应该使用TThread.OnTerminate事件来了解线程何时消失,但仍然远离TThread.WaitFor(),执行您自己的错误处理,例如:

procedure TForm1.Button1Click(Sender: TObject);  
begin
  ProccesSupervisor := TMyThread0.Create(True);
  ProccesSupervisor.FreeOnTerminate := True;
  ProccesSupervisor.OnTerminate := ThreadTerminated;
  ProccesSupervisor.Resume;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  H: array[0..1] of THandle;
  Msg: TMsg;
begin
  if ProccesSupervisor <> nil then
  begin
    ProccesSupervisor.Terminate;
    //ProccesSupervisor.WaitFor;

    H[0] := ProccesSupervisor.Handle;
    H[1] := Classes.SyncEvent;
    WaitResult := 0;
    repeat
      case MsgWaitForMultipleObjects(2, H, False, INFINITE, QS_SENDMESSAGE) of
        WAIT_OBJECT_0, WAIT_FAILED: Break;
        WAIT_OBJECT_0 + 1: CheckSynchronize;
        WAIT_OBJECT_0 + 2: PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
      end;
    until ProccesSupervisor = nil;
  end;
end;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin
  ProccesSupervisor := nil;
end;

procedure TMyThread0.Execute;
begin
  while not Terminated do
  begin
    //some code here
  end;
end;