首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > .NET >

请问Thread的释放有关问题

2012-02-19 
请教Thread的释放问题最近常常遇到线程,线程的释放的问题让我一直拿捏不准。今天写了一个简单的线程,本来还

请教Thread的释放问题
最近常常遇到线程,线程的释放的问题让我一直拿捏不准。今天写了一个简单的线程,本来还从中学了点东西,但是最后遭遇到一个大问题,就是主线程要Destroy的时候,尝试释放线程就失效了,用FastMM以检测,线程对象、线程中手动申请的内存完全释放不了。但是,可以先调用Termiante,然后调用Close关闭掉主窗口,关掉程序就没有问题,于是,我目前只好做成尝试关掉主窗口前强制手动Termiante。
线程类代码

Delphi(Pascal) code
unit UThread;interfaceuses  Classes, Dialogs, Windows, SysUtils;type  TMyThread = class(TThread)  private    FMainHandle: THandle;    FNum: Integer;    FAnswer: Cardinal;    FTimeElapse: Cardinal;    FShowLst: TList;    procedure DoOnTerminate(Sender: TObject);    procedure DisplayAnswer;  protected    procedure Execute; override;  public    constructor create(Suspended: Boolean; Vol: Cardinal);    destructor  destroy; override;    property Terminated;    property MainHandle: THandle read FMainHandle write FMainHandle;  end;implementationuses  UMain;{ TMyThread }constructor TMyThread.create(Suspended: Boolean; Vol: Cardinal);begin  FNum := Vol;  FreeOnTerminate := True;  OnTerminate := DoOnTerminate;  inherited Create(Suspended);  FShowLst := TList.Create;end;destructor TMyThread.destroy;begin  FreeAndNil(FShowLst);  inherited;end;procedure TMyThread.DisplayAnswer;begin  Form1.edt1.Text := IntToStr(FAnswer);  if Form1.pb1.Position < Form1.pb1.Max then    Form1.pb1.StepIt; end;procedure TMyThread.DoOnTerminate(Sender: TObject);begin  begin    MessageBox(Form1.Handle, PChar(Format('Total Time Elapse:%d', [FTimeElapse])),      'Info', 0);    Form1.pb1.Position := 1;    Form1.btn1.Enabled := True;  end;end;procedure TMyThread.Execute;var  i: Integer;  Start_TickCount, k: Cardinal;begin  Start_TickCount := GetTickCount;  for i := 0 to FNum - 1 do  begin    k := Round(Abs(Sin(Sqrt(i))));    if Terminated then      Break;    FAnswer := FAnswer + k;    Synchronize(DisplayAnswer);  end;  FTimeElapse := GetTickCount - Start_TickCount;  inherited;end;end.

主线程代码
Delphi(Pascal) code
unit UMain;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls, UThread, ComCtrls, Buttons;type  TForm1 = class(TForm)    btnCreateSuspend: TButton;    edtDisplay: TEdit;    pb1: TProgressBar;    btnSuspend: TButton;    btnResume: TButton;    btnTerminate: TButton;    btnCheckThreadStatus: TButton;    btn6: TSpeedButton;    procedure btnCreateSuspendClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure btnSuspendClick(Sender: TObject);    procedure btnTerminateClick(Sender: TObject);    procedure btnResumeClick(Sender: TObject);    procedure btnCheckThreadStatusClick(Sender: TObject);    procedure FormDestroy(Sender: TObject);    procedure btn7Click(Sender: TObject);    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);  private    { Private declarations }    procedure TerminateThread;  public    { Public declarations }    FThread: TMyThread;  end;var  Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btnCreateSuspendClick(Sender: TObject);var  Vol: Integer;begin  Vol := 200000;  FThread := TMyThread.create(True, Vol);  pb1.Max := Vol;  TButton(Sender).Enabled := False;end;procedure TForm1.FormCreate(Sender: TObject);begin  pb1.Step := 1;end;procedure TForm1.btnSuspendClick(Sender: TObject);begin  if not FThread.Terminated then  begin    FThread.Suspended := not FThread.Suspended;  end;end;procedure TForm1.btnTerminateClick(Sender: TObject);begin  TerminateThread;end;procedure TForm1.btnResumeClick(Sender: TObject);begin  if Assigned(FThread) and (not FThread.Terminated) and (FThread.Suspended) then    FThread.Resume;end;procedure TForm1.btnCheckThreadStatusClick(Sender: TObject);var  sStatus: string;begin  if not Assigned(FThread) then  begin    sStatus := 'Thread is nil!';    btn6.Caption := 'Thread Current Status: ' + sStatus;    Exit;  end;  if FThread.Suspended then  begin    sStatus := 'Is Suspended: Yes ;';  end  else    sStatus := 'Is Suspended: No ;';  if FThread.Terminated then  begin    sStatus := sStatus + ' Is Terminated: Yes';  end  else    sStatus := sStatus + ' Is Terminated: No';  btn6.Caption := 'Thread Current Status: ' + sStatus;end;procedure TForm1.FormDestroy(Sender: TObject);begin  try    TerminateThread;  except  end;end;procedure TForm1.TerminateThread;begin  if Assigned(FThread) and (not FThread.Terminated) then  begin    if FThread.Suspended then    begin      FThread.Resume;    end;    FThread.Terminate;    FThread := nil;  end;end;{没办法,强制Terminate线程才允许关闭窗口,有什么办法可以解决这个问题?}procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);begin  CanClose := (FThread = nil) or (FThread.Terminated = True);  if not CanClose  then    MessageBox(Handle, 'Termiante the thread first!', ' Terminate thread', 0);end;end. 


代码贴得比较多,但是其实很简单,我现在就是拿捏不准,在主线程Destroy的时候调用TerminateThread完全没有用,根本没有释放线程相关的资源,但是点击btnTerminate就可以正常的释放,调用的是相同的代码。最后,我发现,在主线程Destroy的时候,如果把线程类定义中的OnTerminate := DoOnTerminate;这一句屏蔽掉也正常,但是这屏蔽了正常的执行线程就有问题了,不能及时的刷新窗口,也许你会说在Execute中同步一个方法,但是我如果就想在OnTerminate中实现该怎样处理?或者说怎样通知线程在主线程要Destroy的时候就不要再刷新窗口了,我尝试在OnTerminate的赋值方法中调用类似
if not (csDestroying in Form1.ComponentState) then
  //刷新UI
但是也没有收到相应的效果。
这个到底该怎么办呢?


[解决办法]
你在窗口销毁后,再调用窗口的方法等操作,当然出错了
[解决办法]
在TForm1.FormDestroy里设置一个循环判断线程是否停止和释放,如果没有停止的话则使用TThread.Terminate;再使用TThread.Free;就可以了的。
没必要弄的你那么麻烦,又是调用这个,又是调用那个的
[解决办法]
比如这样应该可以的吧
procedure TMyThread.DisplayAnswer;
begin
if (nil <> Form1) then
begin
Form1.edt1.Text := IntToStr(FAnswer);
if Form1.pb1.Position < Form1.pb1.Max then
Form1.pb1.StepIt;
end;
end;

procedure TMyThread.DoOnTerminate(Sender: TObject);
begin
if (nil <> Form1) then
begin
MessageBox(Form1.Handle, PChar(Format('Total Time Elapse:%d', [FTimeElapse])),
'Info', 0);
Form1.pb1.Position := 1;
Form1.btn1.Enabled := True;
end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Form1 := nil;
TerminateThread;
end;

热点排行