线程执行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.

解决方案 »

  1.   

    你的线程是不是还没有执行完毕
    FTestThread.FreeOnTerminate := True; 线程执行完后,自动释放
    TThread(的子类) 有特殊性, 很多时候我们不能确定新建的线程什么时候执行完(也就是什么时候该释放),所以会设置FreeOnTerminate ,待他执行完后自动释放不知这个解释是否正确
      

  2.   

    设置了线程的FreeOnTerminate := True;在另一个按钮里执行了线程的Terminate,我跟踪看执行过Destroy了
      

  3.   

    是的,你执行了Terminate以后,线程才Free的,你从头到尾也就Terminate了一次,你在加个按钮再做一次Terminate,相信一定会报错..另外如果没记错的话,对canvas操作的话是不需要Synchronize的.还有前面的inherited是用来干吗的?
      

  4.   

    在 Canvas 中使用 Lock 和 Unlock     Form1.Canvas.Lock;
        Form1.Canvas.TextOut(10, 10, IntToStr(i));
        Form1.Canvas.Unlock;
      

  5.   

    线程执行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的过程,会等待线程结束。
      

  6.   

    我知道,线程terminate线程不一定执行完毕, 我是等线程完毕后用取的Terminated属性.
    按照你说的那样就是说,Free后那块内存在没有被其他东西占用的话,还是保留着那个线程的一些值是吗?
    如果这样的话,说Free会释放对象占用的内存是什么意思呢?
      

  7.   

       Free释放掉内存只是告诉Delphi的内存管理器,这块内存我用完了,你可以分配给其它需要内存的地方了。如果没有分配给其它的,你原来的内容是不会消失的。
      

  8.   

    那块内存会被其他的对象占用掉吗?我看每个对象生成的地址在编译后已经是固定的了.
    我用  lstThreadHandle.Items.Add(IntToStr(Integer(@FTestThread)));
    显示FTestThread的内存地址,一直都是一个数值加入了几个其他的动态创建的edit对象后,发现他们的内存地址都是固定的甚至动态控件数组的第一个控件的地址也是固定不变的
      

  9.   


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

  10.   

    当然会被其他对象占用。看看这两段代码的运行结果:
    代码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》里面的,可惜,我一直没有找全所有的文章。)
      

  11.   

    更正:
    FThread当然是一样的了。而FThread是不一样的->@FThread是一样的,FThread是不一样的。笔误
      

  12.   

    delphi中的线程类是不可以立即结束,必须执行完当前的处理。
    看以查看一下delphi中的线程函数。
    FOnterminate 仅仅是一个事件而已。
    参看一下Classes下面的TThread源码。
      

  13.   

    有兴趣的话,可以看一下 windows核心编程 里对windows内存管理的介绍。
    简单来说,之所以访问没有出错,是因为windows对虚拟地址的访问保护是按页面进行的。在x86平台中,一个页面的大小通常是4k,也就是说,向系统申请一块未被使用的虚拟地址的时候,windows会返回一个至少4k大小的页面,而整个4k空间页面的访问保护属性是相同的。而 TThread.InstanceSize 一般只有60字节左右,所以即使不考虑内存管理器的具体实现方式,当释放一个TThread的继承类实例的时候,也只有一个很小的概率会可以把它所占用的页面还给操作系统。也就是说,它所占用的页面极可能还是原有的保护属性,刚刚释放后直接对原实例所在地址进行访问的出错概率极低。
      

  14.   

    to: Seamour 
    呵呵,刚买了一本还没动呢,刚才看了一下,实在是看不懂。
    to:etomahawk 
    你说的有道理,不过你的那两段代码在我机器上显示的结果是一样的,我又分配了一个PChar结果就不一样了。我看Delphi原子世界上是这么说的,FThread也是一个指针地址,但是不用FThread^.这样调用,编译器内建支持的。
      

  15.   

    有这么凑巧?你在按钮1的Click事件中写代码1,按钮2的Click事件中写代码2,这样也是一样吗?反正我这里结果是不一样的。上班...  (-_-)
      

  16.   

    Free只是清除引用的对象内存的分配记录。。引用(指针)如果没有越界,越出进程的地址空间,则所指向的内存块还是有效的。使用起来不报错,只是内容可能是乱码而已,现在内容没有乱码,只是表示那里的内存没有被其他变量或对象写入。