如果我从主线程创建(挂起)线程,如下:
with TMyThread.Create(True) do begin OnTerminate := ThreadTerminated; FreeOnTerminate := False; Start; end;
一旦完成,我该如何释放该实例? (即执行过程已完成执行 – 假设我已捕获异常).
这个Proper way of destroying a tthread object链接显示了一种方法(通过PostMessage程序),它工作正常并且有意义.但是,如果我创建线程并且没有表单的句柄或者我可以调用PostMessage过程的东西,该怎么办?例如,我在直接从TObject下降的类中创建线程?
TMyClass = class public procedure DoSomething; end; TMyClass.DoSomething; begin with TMyThread.Create(True) do begin OnTerminate := ThreadTerminated; FreeOnTerminate := False; Start; end; end;
所以,我想,如何在不访问表单句柄的情况下释放一个线程?
谢谢
解决方法
显然,某处必须有对实例化线程的引用.但我可以提出你的愿望:你想要一个永远不做的解决方案.
我建议你通过一个单独的ThreadController类来管理线程的存在:
unit Unit2; interface uses Classes,SysUtils,Forms,Windows,Messages; type TMyThreadProgressEvent = procedure(Value: Integer; Proceed: Boolean) of object; procedure RunMyThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent); implementation type TMyThread = class(TThread) private FException: Exception; FOnProgress: TMyThreadProgressEvent; FProceed: Boolean; FValue: Integer; procedure DoProgress; procedure HandleException; procedure ShowException; protected procedure Execute; override; end; TMyThreadController = class(TObject) private FThreads: TList; procedure StartThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent); procedure ThreadTerminate(Sender: TObject); public constructor Create; destructor Destroy; override; end; var FMyThreadController: TMyThreadController; function MyThreadController: TMyThreadController; begin if not Assigned(FMyThreadController) then FMyThreadController := TMyThreadController.Create; Result := FMyThreadController end; procedure RunMyThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent); begin MyThreadController.StartThread(StartValue,OnProgress); end; { TMyThreadController } constructor TMyThreadController.Create; begin inherited; FThreads := TList.Create; end; destructor TMyThreadController.Destroy; var Thread: TThread; begin while FThreads.Count > 0 do begin Thread := FThreads[0]; //Save reference because Terminate indirectly //extracts the list entry in OnTerminate! Thread.Terminate; //Indirectly decreases FThreads.Count Thread.Free; end; FThreads.Free; inherited Destroy; end; procedure TMyThreadController.StartThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent); var Thread: TMyThread; begin Thread := TMyThread.Create(True); FThreads.Add(Thread); //Add to list before a call to Resume because once //resumed,the thread might be gone already! Thread.FValue := StartValue; Thread.FOnProgress := OnProgress; Thread.OnTerminate := ThreadTerminate; Thread.Resume; end; procedure TMyThreadController.ThreadTerminate(Sender: TObject); begin FThreads.Extract(Sender); end; { TMyThread } procedure TMyThread.DoProgress; begin if (not Application.Terminated) and Assigned(FOnProgress) then FOnProgress(FValue,FProceed); end; procedure TMyThread.Execute; begin try FProceed := True; while (not Terminated) and (not Application.Terminated) and FProceed and (FValue < 20) do begin Synchronize(DoProgress); if not FProceed then Break; Inc(FValue); Sleep(2000); end; //In case of normal execution ending,the thread may free itself. Otherwise,//the thread controller object frees the thread. if not Terminated then FreeOnTerminate := True; except HandleException; end; end; procedure TMyThread.HandleException; begin FException := Exception(ExceptObject); try if not (FException is EAbort) then Synchronize(ShowException); finally FException := nil; end; end; procedure TMyThread.ShowException; begin if GetCapture <> 0 then SendMessage(GetCapture,WM_CANCELMODE,0); if (FException is Exception) and (not Application.Terminated) then Application.ShowException(FException) else SysUtils.ShowException(FException,nil); end; initialization finalization FreeAndNil(FMyThreadController); end.
要运行此示例线程,以2秒的间隔从5到19计数,并提供反馈和提前终止的机会,请从主线程调用:
type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure MyThreadProgress(Value: Integer; Proceed: Boolean); end; ... procedure TForm1.Button1Click(Sender: TObject); begin RunMyThread(5,MyThreadProgress); end; procedure TForm1.MyThreadProgress(Value: Integer; Proceed: Boolean); begin Caption := IntToStr(Value); end;
该线程自动终止线程或应用程序的终止.
也许这个单位对你的情况有点矫枉过正,因为它能够处理多个线程(相同类型),但我认为它回答了你的问题.根据自己的喜好调整.
这个答案的部分来源:NLDelphi.com.