2011-04-05 96 views
1

下载源代码编译的可执行文件(221 KB(226925个字节)):前http://www.eyeclaxton.com/download/delphi/skeleton.zip线程摧毁永远不会被执行

为什么不一样,如果我关闭应用程序的破坏析构函数被调用(单击X按钮)该线程已终止? FastMM4使用FPauseEvent事件报告内存泄漏。

我应该如何销毁线程?如果有人在线程结束之前关闭应用程序。

unit SkeletonThread; 

interface 

uses 
    Windows, Classes, SysUtils, SyncObjs; 

type 
    TOnInitialize = procedure(Sender: TObject; const AMaxValue: Integer) of object; 
    TOnBegin = procedure(Sender: TObject) of object; 
    TOnProgress = procedure(Sender: TObject; const APosition: Integer) of object; 
    TOnPause = procedure(Sender: TObject; const APaused: Boolean) of object; 
    TOnFinish = procedure(Sender: TObject) of object; 
    TOnFinalize = procedure(Sender: TObject) of object; 

    TMasterThread = class(TThread) 
    private 
    { Private declarations } 
    FPaused: Boolean; 
    FPosition: Integer; 
    FMaxValue: Integer; 
    FOnBegin: TOnBegin; 
    FOnProgress: TOnProgress; 
    FOnFinish: TOnFinish; 
    FOnInitialize: TOnInitialize; 
    FOnFinalize: TOnFinalize; 
    FPauseEvent: TEvent; 
    FOnPause: TOnPause; 
    procedure BeginEvent(); 
    procedure ProgressEvent(); 
    procedure FinishEvent(); 
    procedure InitializeEvent(); 
    procedure FinalizeEvent(); 
    procedure PauseEvent(); 
    procedure CheckForPause(); 
    protected 
    { Protected declarations } 
    procedure DoInitializeEvent(const AMaxValue: Integer); virtual; 
    procedure DoBeginEvent(); virtual; 
    procedure DoProgress(const APosition: Integer); virtual; 
    procedure DoPauseEvent(const APaused: Boolean); virtual; 
    procedure DoFinishEvent(); virtual; 
    procedure DoFinalizeEvent(); virtual; 
    public 
    { Public declarations } 
    constructor Create(const CreateSuspended: Boolean; const theValue: Integer); 
    destructor Destroy(); override; 
    procedure Pause(); 
    procedure Unpause(); 
    published 
    { Published declarations } 
    property IsPaused: Boolean read FPaused write FPaused default False; 
    property OnInitialize: TOnInitialize read FOnInitialize write FOnInitialize default nil; 
    property OnBegin: TOnBegin read FOnBegin write FOnBegin default nil; 
    property OnProgress: TOnProgress read FOnProgress write FOnProgress default nil; 
    property OnPause: TOnPause read FOnPause write FOnPause default nil; 
    property OnFinish: TOnFinish read FOnFinish write FOnFinish default nil; 
    property OnFinalize: TOnFinalize read FOnFinalize write FOnFinalize default nil; 
    end; 

    TSkeletonThread = class(TMasterThread) 
    private 
    { Private declarations } 
    procedure DoExecute(const theValue: Integer); 
    protected 
    { Protected declarations } 
    procedure Execute(); override; 
    public 
    { Public declarations } 
    published 
    { Published declarations } 
    end; 

implementation 

{ TMasterThread } 
constructor TMasterThread.Create(const CreateSuspended: Boolean; const theValue: Integer); 
begin 
    inherited Create(CreateSuspended); 
    Self.FreeOnTerminate := True; 

    Self.FPosition := 0; 
    Self.FMaxValue := theValue; 
    Self.FPaused := False; 
    Self.FPauseEvent := TEvent.Create(nil, True, True, ''); 
end; 

destructor TMasterThread.Destroy(); 
begin 
    FreeAndNil(FPauseEvent); 
    if (Pointer(FPauseEvent) <> nil) then Pointer(FPauseEvent) := nil; 
    inherited Destroy(); 
end; 

procedure TMasterThread.DoBeginEvent(); 
begin 
    if Assigned(Self.FOnBegin) then Self.FOnBegin(Self); 
end; 

procedure TMasterThread.BeginEvent(); 
begin 
    Self.DoBeginEvent(); 
end; 

procedure TMasterThread.DoProgress(const APosition: Integer); 
begin 
    if Assigned(Self.FOnProgress) then Self.FOnProgress(Self, APosition); 
end; 

procedure TMasterThread.ProgressEvent(); 
begin 
    Self.DoProgress(Self.FPosition); 
end; 

procedure TMasterThread.DoFinishEvent(); 
begin 
    if Assigned(Self.FOnFinish) then Self.FOnFinish(Self); 
end; 

procedure TMasterThread.FinishEvent(); 
begin 
    Self.DoFinishEvent(); 
end; 

procedure TMasterThread.DoInitializeEvent(const AMaxValue: Integer); 
begin 
    if Assigned(Self.FOnInitialize) then Self.FOnInitialize(Self, AMaxValue); 
end; 

procedure TMasterThread.InitializeEvent(); 
begin 
    Self.DoInitializeEvent(Self.FMaxValue); 
end; 

procedure TMasterThread.DoFinalizeEvent(); 
begin 
    if Assigned(Self.FOnFinalize) then Self.FOnFinalize(Self); 
end; 

procedure TMasterThread.FinalizeEvent; 
begin 
    Self.DoFinalizeEvent(); 
end; 

procedure TMasterThread.DoPauseEvent(const APaused: Boolean); 
begin 
    if Assigned(Self.FOnPause) then Self.FOnPause(Self, APaused); 
end; 

procedure TMasterThread.PauseEvent(); 
begin 
    Self.DoPauseEvent(Self.FPaused); 
end; 

procedure TMasterThread.Pause(); 
begin 
    Self.FPauseEvent.ResetEvent(); 
    Self.FPaused := True; 
    Self.Synchronize(Self.PauseEvent); 
end; 

procedure TMasterThread.Unpause(); 
begin 
    Self.FPaused := False; 
    Self.Synchronize(Self.PauseEvent); 
    Self.FPauseEvent.SetEvent(); 
end; 

procedure TMasterThread.CheckForPause(); 
begin 
    if (not (Self.Terminated)) then Windows.Sleep(1); 
    Self.FPauseEvent.WaitFor(INFINITE); 
end; 

{ TSkeletonThread } 
procedure TSkeletonThread.DoExecute(const theValue: Integer); 
var 
    X: Integer; 
begin 
    Self.Synchronize(InitializeEvent); 
    try 
    Self.Synchronize(BeginEvent); 
    try 
     for X := 0 to (theValue - 1) do 
     begin 
    Self.CheckForPause(); 

    if (not Self.FPaused) and (not Self.Terminated) then 
    begin 
     Self.FPosition := Self.FPosition + 1; 
     Self.Synchronize(ProgressEvent); 
    end 
    else begin 
     Break; 
    end; 
     end; 

     for X := Self.FPosition downto 1 do 
     begin 
    Self.CheckForPause(); 

    if (not Self.FPaused) and (not Self.Terminated) then 
    begin 
     Self.FPosition := X; 
     Self.Synchronize(ProgressEvent); 
    end 
    else begin 
     Break; 
    end; 
     end; 
    finally 
     Self.Synchronize(FinishEvent); 
    end; 
    finally 
    Self.Synchronize(FinalizeEvent); 
    end; 
end; 

procedure TSkeletonThread.Execute(); 
begin 
    Self.DoExecute(Self.FMaxValue); 
end; 

end. 
+0

不回答,但只是一个想法。在你的代码中使用单独的线程有什么意义,如果你做的所有事情都是在主线程中执行的(你正在使用同步)? – Linas 2011-04-05 11:09:36

+5

是的,我总是从陌生人下载并运行.exe的。 – cHao 2011-04-05 11:10:36

+0

当你关闭应用程序时,你是在线程上调用Terminate并等待它停止? – 2011-04-05 11:11:03

回答

2

你必须自己终止线程(告诉它停止)。一种方法是使用线程的Terminate过程,但您必须在线程Execute方法中检查这一点。这样的事情:

procedure Execute; 
begin 
    inherited; 

    while not Terminated do 
    begin 
    // do your job 
    end; 
end; 

procedure TForm1.StopThread; 
begin 
    MyThread.Terminate; 

    // wait and block until the scheduling thread is finished 
    AResult := WaitForSingleObject(MyThread.Handle, cShutdownTimeout); 

    // check if we timed out 
    if AResult = WAIT_TIMEOUT then 
    TerminateThread(MyThread.Handle, 0); 
end; 

或者你可以使用信号建立到窗口,所以你不必循环。

procedure Execute; 
begin 
    inherited; 

    while not Terminated do 
    begin 
    WaitStatus := WaitForSingleObject(FTermEvent, Max(0, SleepInterval)); 

    // check what was the cause for signalization 
    if WaitStatus <> WAIT_TIMEOUT then 
     Terminate; 
    end; 
end; 

procedure TForm1.StopThread; 
begin 
    // Terminate the thread 
    SetEvent(FTermEvent); 
    // close the handle 
    CloseHandle(FTermEvent); 

    // wait and block until the scheduling thread is finished 
    AResult := WaitForSingleObject(MyThread.Handle, cShutdownTimeout); 

    // check if we timed out 
    if AResult = WAIT_TIMEOUT then 
    TerminateThread(MyThread.Handle, 0); 
end; 

灯号可以终止信令非常简洁的方式,因为你可以使用WaitForMultipleObjects的和释放等待在不同的条件。我使用WaitForSingleObject来使事情不复杂。

另外一定要在线程构造函数中设置“FreeOnTerminate:= True”。哦,当然,最后的硬终端是可选的。这可能是危险的。如果你会使用它,你最了解自己。如果您确定线程最终会停止,您还可以等待更长的时间或无限的时间。

+0

当然它确实解释了。它永远不会被调用,因为线程在应用程序之前未被销毁。当你关闭你的应用程序时,线程不会自行破坏。是的,当操作系统破坏这个过程时,他们最终会被清理干净,但是你的调试器永远不会看到这一点。 – Runner 2011-04-05 12:07:45

+0

嗯,很明显我引用的评论已被删除:) – Runner 2011-04-05 12:08:23

+0

好的,如果你在delphi IDE中运行应用程序,在TMasterThread.Destroy()的第一行添加一个断点,点击main中的“start”按钮形成并等待线程完成后,您会看到程序将在您的中断点停止。如果你做同样的事情,并且这次在线程结束之前,只需单击关闭按钮,断点将不会被执行。 – eyeClaxton 2011-04-05 12:14:55

1
+0

-1。你为什么这样回答这个问题?检测当前进程的结束与检测外部进程的结束完全不同。 – 2011-04-05 13:58:26

+0

我误解了这个问题,道歉 – Amey 2011-04-05 16:34:08