多线程操作问题:
如:
var
  MainFrm: TMainFrm;
  th    : THComm2PDA;//线程对象
...
procedure TMainFrm.btSendClick(Sender: TObject);
begin
  if th<>nil then th.Terminate;
  th:= THComm2PDA.Create(False);
end;
procedure TMainFrm.btGetClick(Sender: TObject);
begin
  if th<>nil then th.Terminate;
  th:= THComm2PDA.Create(False);
end;
procedure TMainFrm.btStopClick(Sender: TObject);//最郁闷在这儿了
begin
  if th<>nil then th.Terminate;
end;
调试的时候发现创建线程后,每次点击‘btStop’时,th总是为线程对象,难道th.Terminate没有终止线程???
或者要如何才能确保我在2次创建线程时必须终止第一次创建的线程??
没有办法了,该程序用于连接串口,而串口设备为半双工(晕),设备要么处于发送状态,要么处于接收状态,通过线程的Create参数确定设备处于何种状态,如果2次线程创建时上一次线程没有终止,肯定会出错,线程中有串口打开操作等

解决方案 »

  1.   

    terminate后线程不一定终止的,需要在程序中读取terminated状态,然后用代码中止正在执行的代码 。中止后,线程也不是nil的。须要将terminatefree(好像是这样写)属性设为True的,或手工Free掉
      

  2.   

    singun(singun)说得对,我补充一句,将terminatefree(好像是这样写)属性设为True的,或手工Free掉后,还得调用th:=nil,否则th即使free后还是不等于nil。
      

  3.   

    大家可以测试一下,建两个窗体TForm1和TForm2,然后再TForm1中创建Form2(在工程属性自创建Form2去掉),大家重复两次跟踪一下以下代码就知道了form2.Free根本不会使Form2=nil。
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      try
        if form2=nil then
          form2:=TForm2.Create(application);
        form2.ShowModal;
      finally
        form2.Free;
      end;
    end;
      

  4.   

    pengdh 说的没错,Free()并不会把对象指针设为nil,因此Delphi提供了一个函数FreeAndNil()以供调用。线程的Terminate()函数其实只是执行线程的Terminated := True因此楼主若要实现前一个线程退出后再创建新线程的目的,应该在线程的退出事件中写代码。var
      MainFrm: TMainFrm;
      th    : THComm2PDA;//线程对象
    ...
    procedure TMainFrm.btSendClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
      th:= THComm2PDA.Create(False);
      th.OnTerminate := thTerminate;                    // 新增
    end;
    procedure TMainFrm.btGetClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
      th:= THComm2PDA.Create(False);
      th.OnTerminate := thTerminate;                    // 新增
    end;
    procedure TMainFrm.btStopClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
    end;procedure TMainFrm.thTerminate(Sender: TObject);    // 新增
    begin                                               //
      th := nil;                                        //
    end;                                                //
      

  5.   

    按照上面的代码,线程 th 终止后将会调用 thTerminate() 函数。
    但仅仅这样做还不够,如上所说,th.Terminate 等同于 th.Terminated := True;
    因此 th.Terminate 不能造成线程 th 的终止,“和平”的终止线程的方法应该是:线程自己退出 Execute() 函数,该线程即告终止。
    所以要在 THComm2PDA.Execute() 中不时地检测一下自己的 Terminated 属性,如果为 True,就退出函数。
      

  6.   

    up
    不分场合,不分地点,不分时间 大家宣传一下:
    一组数据,日本对中国的依赖程度现排最前,特别是对中国市场的依赖,
    如果中国人1个月不买日货,日本将有数千家企业面临破产。
    如果中国人6个月不买日货,日本将有一半人失业。
    如果中国人1年不买日货,日本经济结构彻底瓦解,日本还能这样嚣张吗!
    你如果是中国人,不用你上战场当炮灰,
    你要做的事很简单,就是不买日货。 
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
    抵制日货网络签名处:http://www.dzrhlm.com/book/
      

  7.   

    應該在
    if th<>nil then
    begin
       th.Terminate;
       FreeAndNil(th);
    end;
      

  8.   

    俺是楼主
    在线程的excute函数中已经检测了Terminated属性了,如果这个属性为true时,excute将会马上结束,问题就在这儿了,结束后线程依然不为nil,
    除了 FreeAndNil(th);还有别的办法吗?
    只能这样吗?
    th.Terminate;
    FreeAndNil(th);
    感谢各位,还有点不明白,直接调用FreeAndNil会不会导致我的线程不会正常结束?如果不是正常结束,还是会有问题的,我在线程中打开了串口。
      

  9.   

    我上面的代码已经写了,线程真正结束时进入 OnTerminate 事件,要在真正结束时将线程指针置nil以方便判断。而不是在 th.Terminate; 后直接写 FreeAndNil(th);
      

  10.   

    to DDGG(叮叮当当)
    你说的办法确实能保证全局线程对象退出后变成nil,可是程序并不能这么写,原因如下
    var
      MainFrm: TMainFrm;
      th    : THComm2PDA;//线程对象
    ...
    procedure TMainFrm.btSendClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
      th:= THComm2PDA.Create(False);
      th.OnTerminate := thTerminate;                    // 新增  
      to DDGG(叮叮当当):问题在这!!
      当运行th.Terminate后,又马上创建了th对象,这时候并不能保证我在创建th之前th为nil!
      在th中需要初始化串口和半双工串口设备,如果上一个th没有结束,初始化肯定会出问题,th中有sleep函数,通过循环检测Terminated属性实现串口的持续数据接收,如果不用线程的话,这个问题可以解决,但是不用线程的结果是主程序将陷入死循环,且桌面停止响应(这个就不用多解释了)
      在线程的excute函数中检测了Terminated属性,线程是可以正常结束的。
    end;
    procedure TMainFrm.btGetClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
      th:= THComm2PDA.Create(False);
      th.OnTerminate := thTerminate;                    // 新增
    end;
    procedure TMainFrm.btStopClick(Sender: TObject);
    begin
      if th<>nil then th.Terminate;
    end;procedure TMainFrm.thTerminate(Sender: TObject);    // 新增
    begin                                               //
      th := nil;                                        //
    end;    
    感谢参与,有再次看到的,欢迎讨论……
    明天结贴
      

  11.   

    或者可以用sendmessage(这东西会等待消息返回)给线程发个消息,以此保证线程全部结束后再创建th对象,这个现在只是个想法而已,明天试试
      

  12.   

    根据楼主所说,关键的问题,就是要保证只有在结束当前的线程之后,才启动新的线程.稍微加以修改,可以得到2个可靠的方法:1. 
    procedure TMainFrm.btSendClick(Sender: TObject);
       if th<>nil then th.Terminate;   th.WaitFor;                     //增加的代码,等待线程结束
       FreeAndNil(th);                 //如果th.FreeOnTerminate则用th=nil  th:= THComm2PDA.Create(False);
      th.OnTerminate := thTerminate; 2.procedure TMainFrm.thTerminate(Sender: TObject);
    begin
      th := nil;  if ... then begin                       //不一定要创建新的线程,加上适当判断
        th:= THComm2PDA.Create(False);
        th.OnTerminate := thTerminate; 
      end
        
    end;  第一种情况适于线程可以很快结束的情况(<0.5秒),否则会有UI延迟.另外,中止线程还有一种方法:
    对于依赖某种句柄的线程,可以直接在线程外关闭这个句柄,线程自然直接结束,快速有效.另外,任何时候,在线程的Execute中都要有适当的try...except.