为什么线程执行Free后,还能调用它的Terminated属性?

wintergoes 2009-03-04 12:21:00
线程执行Free后内存不是已经释放掉了吗?为什么还能调用它的Terminated属性,还可以调用其他的一些自定义属性.

unit Unit1;

interface

uses
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.
...全文
426 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
wintergoes 2009-03-05
  • 打赏
  • 举报
回复
to: Seamour
呵呵,刚买了一本还没动呢,刚才看了一下,实在是看不懂。


to:etomahawk
你说的有道理,不过你的那两段代码在我机器上显示的结果是一样的,我又分配了一个PChar结果就不一样了。我看Delphi原子世界上是这么说的,FThread也是一个指针地址,但是不用FThread^.这样调用,编译器内建支持的。
cranley 2009-03-05
  • 打赏
  • 举报
回复
Free只是清除引用的对象内存的分配记录。。

引用(指针)如果没有越界,越出进程的地址空间,则所指向的内存块还是有效的。

使用起来不报错,只是内容可能是乱码而已,现在内容没有乱码,只是表示那里的内存没有被其他变量或对象写入。
etomahawk 2009-03-05
  • 打赏
  • 举报
回复
有这么凑巧?你在按钮1的Click事件中写代码1,按钮2的Click事件中写代码2,这样也是一样吗?反正我这里结果是不一样的。

上班... (-_-)
willflyz 2009-03-04
  • 打赏
  • 举报
回复
是的,你执行了Terminate以后,线程才Free的,你从头到尾也就Terminate了一次,你在加个按钮再做一次Terminate,相信一定会报错..

另外如果没记错的话,对canvas操作的话是不需要Synchronize的.还有前面的inherited是用来干吗的?
wintergoes 2009-03-04
  • 打赏
  • 举报
回复
设置了线程的FreeOnTerminate := True;

在另一个按钮里执行了线程的Terminate,我跟踪看执行过Destroy了
starluck 2009-03-04
  • 打赏
  • 举报
回复


你什麼時候FREE了??
bdmh 2009-03-04
  • 打赏
  • 举报
回复
你的线程是不是还没有执行完毕
FTestThread.FreeOnTerminate := True; 线程执行完后,自动释放
TThread(的子类) 有特殊性, 很多时候我们不能确定新建的线程什么时候执行完(也就是什么时候该释放),所以会设置FreeOnTerminate ,待他执行完后自动释放

不知这个解释是否正确
Seamour 2009-03-04
  • 打赏
  • 举报
回复
有兴趣的话,可以看一下 windows核心编程 里对windows内存管理的介绍。
简单来说,之所以访问没有出错,是因为windows对虚拟地址的访问保护是按页面进行的。在x86平台中,一个页面的大小通常是4k,也就是说,向系统申请一块未被使用的虚拟地址的时候,windows会返回一个至少4k大小的页面,而整个4k空间页面的访问保护属性是相同的。而 TThread.InstanceSize 一般只有60字节左右,所以即使不考虑内存管理器的具体实现方式,当释放一个TThread的继承类实例的时候,也只有一个很小的概率会可以把它所占用的页面还给操作系统。也就是说,它所占用的页面极可能还是原有的保护属性,刚刚释放后直接对原实例所在地址进行访问的出错概率极低。
sfwanwxf 2009-03-04
  • 打赏
  • 举报
回复
delphi中的线程类是不可以立即结束,必须执行完当前的处理。
看以查看一下delphi中的线程函数。
FOnterminate 仅仅是一个事件而已。
参看一下Classes下面的TThread源码。
etomahawk 2009-03-04
  • 打赏
  • 举报
回复
更正:
FThread当然是一样的了。而FThread是不一样的

->

@FThread是一样的,FThread是不一样的。

笔误。。。
etomahawk 2009-03-04
  • 打赏
  • 举报
回复
当然会被其他对象占用。

看看这两段代码的运行结果:
代码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》里面的,可惜,我一直没有找全所有的文章。)
wintergoes 2009-03-04
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 liangpei2008 的回复:]
Delphi(Pascal) code
//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;
//其内有一些处理省略…
[/Quote]

这个有什么关系?我主是要说对象释放了,他那块内存会被其他的对象再占用吗?这里是以线程为例子.
liangpei2008 2009-03-04
  • 打赏
  • 举报
回复

//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;



wintergoes 2009-03-04
  • 打赏
  • 举报
回复
那块内存会被其他的对象占用掉吗?我看每个对象生成的地址在编译后已经是固定的了.
我用 lstThreadHandle.Items.Add(IntToStr(Integer(@FTestThread)));
显示FTestThread的内存地址,一直都是一个数值

加入了几个其他的动态创建的edit对象后,发现他们的内存地址都是固定的

甚至动态控件数组的第一个控件的地址也是固定不变的
genispan 2009-03-04
  • 打赏
  • 举报
回复
MARK+学习
etomahawk 2009-03-04
  • 打赏
  • 举报
回复
Free释放掉内存只是告诉Delphi的内存管理器,这块内存我用完了,你可以分配给其它需要内存的地方了。如果没有分配给其它的,你原来的内容是不会消失的。
wintergoes 2009-03-04
  • 打赏
  • 举报
回复
我知道,线程terminate线程不一定执行完毕, 我是等线程完毕后用取的Terminated属性.
按照你说的那样就是说,Free后那块内存在没有被其他东西占用的话,还是保留着那个线程的一些值是吗?
如果这样的话,说Free会释放对象占用的内存是什么意思呢?
etomahawk 2009-03-04
  • 打赏
  • 举报
回复
线程执行Free后内存不是已经释放掉了吗?为什么还能调用它的Terminated属性,还可以调用其他的一些自定义属性.
-----------------------------------------------------------------

内存释放,并不等于不可以读。至于内容是否正确,要看保存属性的内存空间是否已经被其他地方修改。
你可以想像一下线程对象在内存中是如何存放的,你就可以明白为什么了。看下面的代码:
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的过程,会等待线程结束。
bdmh 2009-03-04
  • 打赏
  • 举报
回复
在 Canvas 中使用 Lock 和 Unlock

Form1.Canvas.Lock;
Form1.Canvas.TextOut(10, 10, IntToStr(i));
Form1.Canvas.Unlock;

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧