我在程序里开了两个线程,一个是从ftp服务器上传文件,一个是从ftp服务器下载文件,他们都是2分钟被timer(就一个timer,先唤醒上传线程,再唤醒下载线程)触发一次,然后执行线程中的代码,线程执行完毕自动挂起。现在出现的问题是,线程里与ftp服务器打交道的代码执行过程中可能终止,类似于死循环的概念,导致线程不能正常地退出,于是我在主线程里的timer事件中先检测线程是否处于挂起状态,没有的话,表示线程没有正常执行完毕,我就用TerminateThread函数强行中止线程,再重建它,但这并没有达到预想的效果,反而线程不但没有杀死,反而出现两个同样的上传线程在运行,我需要每过几天就重启一下程序,很麻烦的,按道理程序一旦运行,不需要再干预的。请问这种情况该如何解决?我的程序结构大体如下:
主窗体的formcreate事件  TPut:=tWrite.Create;  //创建上传线程,创建完毕自动处于挂起状态
  TGet:=tgetFile.Create; //创建下载成线程,创建完毕自动处于挂起状态主窗体的ontimer(2分钟触发一次)事件  if not TGet.Suspended then  //2分钟没有执行完毕,强行终止线程并重建
    begin
      TerminateThread(TGet.Handle,0);  
      Sleep(500);
      TGet:=TGetFile.Create;
    end;  if not TPut.Suspended then
    begin      
      TerminateThread(TPut.Handle,0);
      Sleep(500);
      TPut:=TWrite.Create;
    end;  TPut.Resume;
在线程TGetFile的execute代码体如下:
  while not Terminated do
    begin
      try 
        ............
        与ftp服务器打交道;
        ............
      except
      end;      Suspend;
    end;原先在timer事件中设这么写的,
  if not TGet.Suspended then  //2分钟没有执行完毕,则等待它执行完毕,并不强行中止
    Exit;  if not TPut.Suspended then
    Exit;  TPut.Resume;这种情况发现过一段时间后,程序会出现莫名其妙的挂起现象,于是采用强行终止线程的做法。
请问是否有其它的强行终止线程的方法?
我的代码设计逻辑上那些地方应该有所改进,谢谢。我的另一个帖子在
http://community.csdn.net/Expert/topic/3541/3541560.xml?temp=.8955194如果大家还不明白我说的情况,我这几天查了一下以往的帖子,下面这个帖子与我说的情形类似
http://search.csdn.net/Expert/topic/457/457080.xml?temp=.9097559请大家帮忙看下,如果程序在运行几天没问题后,我会自动结帖的,谢谢。

解决方案 »

  1.   

    看上去似乎是楼主代码的结构设计有关。DELPHI标准的线程代码是:
      while not Terminated do
        begin
          ;
        end;
    不过从我的实际经验看,一旦线程处于某些不正常状态(比如一直在等待回应导致事实上的挂起),好像就无法接收到主窗体发送的Terminate信号。所以我建议楼主在线程的public中加入自己的两个信号变量IsTerm和IsWork,以取代Terminate,即
        while not IsTerm do         //线程循环开始
        begin
            if IsWork then          
            begin
                DoSomething;        //工作
            end
            else
                Sleep(1000);        //如果不处工作状态,则不工作,但是线程不中止
        end;IsTerm用来控制是否终止线程,IsWork用来控制线程是否工作。这样至少会有如下两个好处:
    1、将线程启动状态和工作状态完全分开,两者可以完全从主程序分别加以控制;
    2、只要你线程中的DoSomething部分没有停止响应而不返回(这点可以通过内部纠错机制加以保证,使之无论如何都能返回),那么线程就能顺利的被你控制了。以上是一点看法,同楼主探讨,呵呵。
      

  2.   

    补充一点:
    使用这样的方式控制线程,不需要使用Terminat、Suspend和Resume。只需要设置IsTerm和IsWork是否为True(如果IsTerm为True,则线程主循环会做完,线程将自动退出),另外在启动的时候直接启动线程就可以了(IsTerm和IsWork会在线程创建的时候被自动赋值为False)。  ^_^
      

  3.   

    我的msn是[email protected],欢迎大家和我探讨,谢谢。
    现在主要想把问题解决掉。
      

  4.   

    我看了一下网友 honestsky(晓彷)  的回复,觉得挺有道理的。
    我程序可以通过计时器来判断线程是否处于 work 状态,但我有一点担心的是程序如果处于非工作状态,那么他可能一直sleep下去,就是不能回到工作状态。我原先在timer事件中设这么写的,
      if not TGet.Suspended then  //2分钟没有执行完毕,则等待它执行完毕,并不强行中止
        Exit;  if not TPut.Suspended then
        Exit;  TPut.Resume;我的这段代码思路和您得差不多,只不过是在线程外部来控制的。我用线程的suspended标志作为线程是否处于 work 状态。如果没有正常挂起(一直处于工作状态),那么就不调用线程中的代码,等到计时器的下一次事件到来。 通过实际的运行情况来看,但这种情况并没有解决我说的问题,如果出现问题后(程序没有处于suspended状态),程序一直处于挂起状态,不能定时地上传和下载文件。我怀疑是不是与 与ftp服务器打交道 的代码有问题。
      

  5.   

    CodeMagic:我的意思是,用自己的信号变量,替代系统的suspended。
    如果程序处于非工作状态,我想你可以随时在主程序中通过设置IsTerm为true来结束线程。我的实际经验告诉我,系统提供的Suspended工作起来似乎不那么可靠,呵呵。
    我不清楚Suspended的工作机制,但是没有检测到系统的Suspended状态,不一定就表示没有挂起,所以用它可能会导致你判断失误。如果你的问题出在这里,那么用IsWork和IsTrem就可以加以避免,因为它们都是在自己的代码中进行完全控制的。另一方面,也许你的问题在于“与ftp服务器打交道的代码”会在某种情况下实际挂起而导致线程死锁,所以我提出你大概需要在那些代码中放入某种异常机制,使得保证它们能在出错的时候也能返回。
      

  6.   

    以前我一個項目中也有類似的要求.
     開始運行一段時間, 線程就會挂起, 隨機的, 也找不到原因, 我也做了個與你類似的,
     不過,在 TerminateThread 之後, 我加了個
     Application.ProcessMessage; //實際這裹延时2秒
     FreeAndNil(MyThread);
     Application.ProcessMessage; //實際這裹延时2秒大概如此, 到現在, 客戶用了兩年, 也沒說出問題!我不在确定會不會內存泄露, 但當時在客戶那里開發, 沒工具測試, 後來, 沒出問題, 也就沒再理了
      

  7.   

    另一方面,也许你的问题在于“与ftp服务器打交道的代码”会在某种情况下实际挂起而导致线程死锁,所以我提出你大概需要在那些代码中放入某种异常机制,使得保证它们能在出错的时候也能返回。
    ==========================
    我加入了异常机制好的,我将按照您的想法来改造一下我的程序。
      

  8.   

    to  aiirii(ari-爱的眼睛):Application.ProcessMessage; //實際這裹延时2秒这句是什么意思
    2秒这个确切数字是怎么得来的,谢谢。
      

  9.   

    兄弟,不是我不结,是问题还没有解决。
    不过不要担心,我肯定会揭帖的,我现在不想开新贴,否则问题又得重复描述一遍。今天我发现原先的上传线程不但没有终止掉,反而又重建了一个,相当于有两个同样的上传线程在跑。你说的办法我正想用呢(最近用的是 aiirii(ari-爱的眼睛) 的方法)。不过我有点疑问:
    我必须将线程挂起,因为2秒钟执行一次。所以我觉得suspended状态和iswork有点冲突,不知道该如何安排他们,如何使用?问题解决,我另加分开贴,这个请放心。
      

  10.   

    呵呵,开个玩笑而已 :)我说的方法里面,用不上suspended了,你没仔细看吗?只要IsTerm没有为true,线程就一直在运行;IsWork带来的区别,仅仅是IsWork为true时,则DoSomething代码工作,IsWork为false,DoSomething代码不工作而已。也就是说,无论DoSomething代码工作不工作,都和线程原来的suspended状态无关了。我的做法,就是打算抛弃线程的suspended,另起炉灶啊。 :)
      

  11.   

    思路不正确,杀掉子线程之类方法是最下等的方法。
    你发出关程序就是通知程序主线程要退出了,主线程的消息循环会时刻检查这个标记的。
    如果你在按钮时间下个死循环也只能杀掉。同理,你的子线程也要守点规矩。基本思路:主线程只要在必要的时候用Thread.Terminate通知子线程需要退出就行了。
              自线程有责任检查退出的标记,尽可能快地退出。
    一个自线程简单的框架,好的框架去看windows的消息循环处理机制吧,好像forms.pas单元有。
    推荐:创建的时候线程设成退出后自动释放线程对象,这样就不用主动释放对象了。
    procedure  execute;
    var
      slowslow: x;
    begin
      ...//  通知主线程对象开始运行了/
      try  //主框架
        while not self.terminated do
        begin
          slowslow := x.create;
          try
            .....
            slowslow.connect(timeout =10s);
            if self.terminated then exit; //时刻准备逃出城
            while not ok and not terminated do
              slowslow.get(timeout =1s);  //把取数据分开成一小块一小块去,控制好超时,这样才能即使响应停止标记,从容退出。
            if self.terminated then exit; 
         //收完数据的处理 
         finally
           slowslow.free;
         end; 
      end;
      except
        //错误记到日志或发给主线程回馈给界面,懒得话简单的丢掉
      end;
      ...//通知主线程线程执行完毕,可以把对象引用去掉了
    end
      

  12.   

    to honestsky(晓彷) :你的意思我明白。但是我必须不停地按照timer的触发间隔resume 和 suspend线程,毕竟2分钟执行一次。也就是说线程里的代码执行完毕,必须把该线程suspend,然后等到timer的触发时间间隔到,再把该线程resume,这点我想是没有问题的,是必须要做的。如果采用的你方法,我在timer事件中用iswork变量判断线程是否工作,如果没有处于work状态,那么我想终止该线程,但是怎么能够调用该线程呢(如果线程没有处于suspend状态,可能内部停了,没有退出该线程的执行代码),这时再调用resume估计不行,因为它本身没有挂起,所以强制终止线程是必须要的。
      

  13.   

    Application.HandleMessage
    or
    Application. ProcessMessage
      

  14.   

    但是我必须不停地按照timer的触发间隔resume 和 suspend线程,毕竟2分钟执行一次。也就是说线程里的代码执行完毕,必须把该线程suspend,然后等到timer的触发时间间隔到,再把该线程resume,这点我想是没有问题的,是必须要做的。觉得SUSPEND不安全的话,那么,你在2分钟内执行完毕,然后自动释放
    TIMER到时间再CREATE吧
      

  15.   

    重复创建有何不好,让线程自己退出至少可以保证内存安全在线程的OnTerminate事件里创建下一个线程或者触发timer,然后等一下在创建新线程
      

  16.   

    suspend;有什么用?突然发现你这个程序有点好笑,不知道是不是我的知识浅薄
    suspend以后,用Resume恢复线程以后,就从suspend那个地方继续执行线程,而不是重新启动线程
    因此你实际上还是在重复创建线程,而且你的线程都无法自动退出
      

  17.   

    你的程序执行过程应该是这样的:
    线程创建--线程执行(ftp)那段代码--线程挂起--timer事件(这时的线程Suspened=True)--线程继续执行并自动完成(销毁)--下一个tiemr事件(这时线程已经销毁,所以Suspended=False)--用TerminateThread再强制销毁一次线程(因为你的线程已经创建过,所以这里并不报错)--接着再创建线程--接着再重复以上过程,我晕
      

  18.   

    suspend以后,用Resume恢复线程以后,就从suspend那个地方继续执行线程,而不是重新启动线程
    ========================================================================
    是呀,这个没错因此你实际上还是在重复创建线程,而且你的线程都无法自动退出
    ======================================================
    我假设线程如果在2分钟内正常执行完毕,他会suspend自己,就处于suspended状态,然后我在timer的事件里判断线程是否挂起(执行完毕),根据线程的suspended标志  如果执行完毕,我就resume它,并没有重复创建
      如果没有suspended,我就destroy它,然后重建这个线程
      

  19.   

    线程创建--线程执行(ftp)那段代码--线程挂起--timer事件(这时的线程Suspened=True)--线程继续执行并自动完成(销毁)
    ==============================================
    请问线程suspend,如果不唤醒它,他怎么能够继续执行呢?
      

  20.   

    你先看清楚自己的程序结构,两种情况:
    a:
    1.FormCreate,线程创建
    2.线程执行(这里假设ftp那一段在两分钟内执行完毕)-执行Suspend
    3.Timer事件,因为Suspended=True,因此执行Resume
    4.线程继续执行,自动完成并销毁
    5.Timer事件,以为线程已经销毁,所以Suspended=False;
      执行
      TerminateThread(TGet.Handle,0);  //还有必要吗?
      Sleep(500);
      TGet:=TGetFile.Create; //创建以后并不立即执行
    6.Timer事件,Resume,跳到2;b:
    1.FormCreate,线程创建
    2.线程执行(这里假设ftp那一段在两分钟内未执行完毕)
    3.Timer事件,Suspended=False;
      执行
      TerminateThread(TGet.Handle,0);  //还有必要吗?
      Sleep(500);
      TGet:=TGetFile.Create; //创建以后并不立即执行
    4.Timer事件,Resume,跳到2;就是说,无论ftp过程是否在两分钟内执行完毕,你都在不断的重新创建线程,为什么有这句话呢:
    "那就是老重复创建了,是可以这么做的,关键程序可是24小时不停地跑。"???
      

  21.   

    b情况应该是这样:
    1.FormCreate,线程创建
    2.线程执行(这里假设ftp那一段在两分钟内未执行完毕)
    3.Timer事件,Suspended=False;
      执行
      TerminateThread(TGet.Handle,0);  
      Sleep(500);
      TGet:=TGetFile.Create; //创建以后并不立即执行
      跳到2;
      

  22.   

    重新仔细考虑了一下,又听了听我的同事(他是资深程序员)的意见,觉得我上面说的,大概没用处,对楼主帮助不大。(忘了它吧汗)    楼主真正的问题,还是应该在具体任务的那段代码上,在于“与ftp服务器打交道的代码”会在某种情况下实际挂起而导致线程死锁,这个核心的问题必须得到解决,否则你对线程就失去了控制,在线程失控的情况下,使用何种线程控制方式,在事实上应该都是无效的。    laagee()兄说的:“//把取数据分开成一小块一小块去,控制好超时,这样才能即使响应停止标记,从容退出”不失为一个解决你的问题的思路,还是别太关心怎么去控制你的线程了,让你的关键代码在发生异常时也不死锁才该是你最关心的。
      

  23.   

    好的,我再看看有关与ftp server打交道的代码吧