场景1)当我点击button1然后在线程正常工作时用X关闭表单时,我得到"线程错误:句柄无效"
场景2)当我在没有点击button1的情况下关闭应用程序时,我得到了#34;访问违规..."
if (!strcmp(myName, "Program1"))
printf("I am program 1!");
else
printf("I am someone else !");
答案 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;