线程执行Free后内存不是已经释放掉了吗?为什么还能调用它的Terminated属性,还可以调用其他的一些自定义属性.unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;type
TTestThread = class(TThread)
private
procedure DrawLine;
procedure Execute; override;
public
FStr: string;
destructor Destroy; override;
end;type
TForm1 = class(TForm)
imgTest: TImage;
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
edtFreeTest: TEdit;
lstThreadHandle: TListBox;
Button8: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
private
FTestThread: TTestThread;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}{ TTestThread }destructor TTestThread.Destroy;
begin
FStr := 'Is Destroyed';
inherited;
end;procedure TTestThread.DrawLine;
var
I, J: Integer;
begin
Form1.imgTest.Canvas.FillRect(Form1.imgTest.Canvas.ClipRect);
for I := 0 to Form1.imgTest.Width do
begin
for J := 0 to Form1.imgTest.Height do
begin
Application.ProcessMessages ;
Randomize;
Form1.imgTest.Canvas.Pixels[I, J] := RGB(Random(255), Random(255), Random(255));
end;
end;
end;procedure TTestThread.Execute;begin
inherited;
while not Terminated do
begin
Synchronize(DrawLine);
end;
end;procedure TForm1.Button1Click(Sender: TObject);
begin
FTestThread := TTestThread.Create(True);
FTestThread.FreeOnTerminate := True;
FTestThread.FStr := 'Runing';
FTestThread.Resume;
end;procedure TForm1.Button2Click(Sender: TObject);
begin
FTestThread.Terminate;
end;procedure TForm1.Button4Click(Sender: TObject);
begin
if Assigned(FTestThread) then
ShowMessage('Thread is not nil')
else
ShowMessage('Thread is nil')
end;procedure TForm1.Button5Click(Sender: TObject);
begin
if FTestThread.Terminated then
ShowMessage('Thread is Terminate')
else
ShowMessage('Thread is not Terminate')
end;procedure TForm1.Button6Click(Sender: TObject);
begin
ShowMessage(FTestThread.FStr);
end;procedure TForm1.Button8Click(Sender: TObject);
begin
lstThreadHandle.Items.Add(IntToStr(Integer(@FTestThread.Handle)));
end;
end.
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;type
TTestThread = class(TThread)
private
procedure DrawLine;
procedure Execute; override;
public
FStr: string;
destructor Destroy; override;
end;type
TForm1 = class(TForm)
imgTest: TImage;
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
edtFreeTest: TEdit;
lstThreadHandle: TListBox;
Button8: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
private
FTestThread: TTestThread;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}{ TTestThread }destructor TTestThread.Destroy;
begin
FStr := 'Is Destroyed';
inherited;
end;procedure TTestThread.DrawLine;
var
I, J: Integer;
begin
Form1.imgTest.Canvas.FillRect(Form1.imgTest.Canvas.ClipRect);
for I := 0 to Form1.imgTest.Width do
begin
for J := 0 to Form1.imgTest.Height do
begin
Application.ProcessMessages ;
Randomize;
Form1.imgTest.Canvas.Pixels[I, J] := RGB(Random(255), Random(255), Random(255));
end;
end;
end;procedure TTestThread.Execute;begin
inherited;
while not Terminated do
begin
Synchronize(DrawLine);
end;
end;procedure TForm1.Button1Click(Sender: TObject);
begin
FTestThread := TTestThread.Create(True);
FTestThread.FreeOnTerminate := True;
FTestThread.FStr := 'Runing';
FTestThread.Resume;
end;procedure TForm1.Button2Click(Sender: TObject);
begin
FTestThread.Terminate;
end;procedure TForm1.Button4Click(Sender: TObject);
begin
if Assigned(FTestThread) then
ShowMessage('Thread is not nil')
else
ShowMessage('Thread is nil')
end;procedure TForm1.Button5Click(Sender: TObject);
begin
if FTestThread.Terminated then
ShowMessage('Thread is Terminate')
else
ShowMessage('Thread is not Terminate')
end;procedure TForm1.Button6Click(Sender: TObject);
begin
ShowMessage(FTestThread.FStr);
end;procedure TForm1.Button8Click(Sender: TObject);
begin
lstThreadHandle.Items.Add(IntToStr(Integer(@FTestThread.Handle)));
end;
end.
FTestThread.FreeOnTerminate := True; 线程执行完后,自动释放
TThread(的子类) 有特殊性, 很多时候我们不能确定新建的线程什么时候执行完(也就是什么时候该释放),所以会设置FreeOnTerminate ,待他执行完后自动释放不知这个解释是否正确
Form1.Canvas.TextOut(10, 10, IntToStr(i));
Form1.Canvas.Unlock;
----------------------------------------------------------------- 内存释放,并不等于不可以读。至于内容是否正确,要看保存属性的内存空间是否已经被其他地方修改。
你可以想像一下线程对象在内存中是如何存放的,你就可以明白为什么了。看下面的代码:
var
FThread: TThread;
begin
// 我们把Self强制转换成线程对象,可以正确运行,而且不会出错,但是结果没有什么意义
FThread:= Thread(Self);
ShowMessage(IntToStr(FThread.ThreadID)); // 我们把随便一个地址转换成线程对象
FThread:= TThread($123456);
ShowMessage(IntToStr(FThread.ThreadID)); // 这里就可能出错了,因为我们读了一个不可读的地址
end;
照样可以编译通过,只是运行时可能会出错。FThread仅仅是一个指针而已。
因此,一般的做法就是自己控制好线程,用FreeAndNil(FThread);防止这种野指针的出现。 线程Terminate,线程执行函数不一定执行完毕。然而,如果Destroy已经执行过了,那线程函数肯定已经执行完成,因为在Destroy中有个WaitFor的过程,会等待线程结束。
按照你说的那样就是说,Free后那块内存在没有被其他东西占用的话,还是保留着那个线程的一些值是吗?
如果这样的话,说Free会释放对象占用的内存是什么意思呢?
我用 lstThreadHandle.Items.Add(IntToStr(Integer(@FTestThread)));
显示FTestThread的内存地址,一直都是一个数值加入了几个其他的动态创建的edit对象后,发现他们的内存地址都是固定的甚至动态控件数组的第一个控件的地址也是固定不变的
//VCL已经写得很清楚了
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;procedure TThread.Terminate;
begin
FTerminated := True;
end;function ThreadProc(Thread: TThread): Integer;
var
FreeThread: Boolean;
begin
try
if not Thread.Terminated then//
try
Thread.Execute;
except
end;
//其内有一些处理省略
finally
if FreeThread then Thread.Free;//释放在线程函数外创建的堆对象
EndThread(Result);//调用api-exitThread清理线程栈
end;
end;
代码1.
FThread:= TMyThread.Create(false);
FThread.FreeOnTerminate:= true; ListBox1.Items.Add(IntToStr(Integer(FThread)));
ListBox1.Items.Add(IntToStr(Integer(@FThread)));代码2.
var
pMem: Pointer;
begin
GetMem(pMem, 12345); FThread:= TMyThread.Create(false);
FThread.FreeOnTerminate:= true; ListBox1.Items.Add(IntToStr(Integer(FThread)));
ListBox1.Items.Add(IntToStr(Integer(@FThread))); FreeMem(pMem);
结果是否一样?FThread当然是一样的了。而FThread是不一样的,FThread才是指向的地址,@FThread是FThread指针所在的地址。@FThread会随着Form1对象实例的地址变化而变化。
搞清楚这个关系:指针的地址和它所指向的地址是不一样的。var
pBuf: Pointer;
begin
pBuf^ : 访问内容
@pBuf : 取这个指针的地址。 这时一般指针的使用方法。而delphi中对象指针却有点不同。
虽然说FThread也是一个指针,但是你可以使用FThread来访问它指向的内容,而不需要使用FThread^来访问(事实上这样是不允许的),这是编译器支持的,也是必须遵循的。 建议你去看看《Delphi原子世界》这篇文章,看看Delphi对象的生和死,内存布局等,就会明白了。(这篇文章本来是《悟透Delphi》里面的,可惜,我一直没有找全所有的文章。)
FThread当然是一样的了。而FThread是不一样的->@FThread是一样的,FThread是不一样的。笔误
看以查看一下delphi中的线程函数。
FOnterminate 仅仅是一个事件而已。
参看一下Classes下面的TThread源码。
简单来说,之所以访问没有出错,是因为windows对虚拟地址的访问保护是按页面进行的。在x86平台中,一个页面的大小通常是4k,也就是说,向系统申请一块未被使用的虚拟地址的时候,windows会返回一个至少4k大小的页面,而整个4k空间页面的访问保护属性是相同的。而 TThread.InstanceSize 一般只有60字节左右,所以即使不考虑内存管理器的具体实现方式,当释放一个TThread的继承类实例的时候,也只有一个很小的概率会可以把它所占用的页面还给操作系统。也就是说,它所占用的页面极可能还是原有的保护属性,刚刚释放后直接对原实例所在地址进行访问的出错概率极低。
呵呵,刚买了一本还没动呢,刚才看了一下,实在是看不懂。
to:etomahawk
你说的有道理,不过你的那两段代码在我机器上显示的结果是一样的,我又分配了一个PChar结果就不一样了。我看Delphi原子世界上是这么说的,FThread也是一个指针地址,但是不用FThread^.这样调用,编译器内建支持的。