我在程序里开了两个线程,一个是从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请大家帮忙看下,如果程序在运行几天没问题后,我会自动结帖的,谢谢。
主窗体的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请大家帮忙看下,如果程序在运行几天没问题后,我会自动结帖的,谢谢。
解决方案 »
- 问个关于fastreport的问题
- 实在受不了了,忍不住发两句牢骚
- 初学者的ado问题,高手指点。
- 那位兄弟在线,帮我解决一个非常简单的问题!
- 请问webbrowser中使用COOKIE或者SESSION的问题,点入看问题详细描述(UP即给分)
- 为什么GB2312转换到UTF8,中文全部变为问号?
- 请问如何用adoquery.savetofile 方法存为TXT文件??
- 有没有大虾介绍一下 QRChart的使用?有意的进来看看,100分相送!
- access视图中的nz函数在DELPHI中不认,isnull在access中又不认,怎么办呀??????
- [问题]我想把数据库,打成资源文件。可不知道,如何对其进行读写?
- 请问大家winsock编程时,使用的是delphi带的组件还是直接操作windows api?
- 请问怎样动态的为FastReport报表中的文本框附值啊?
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部分没有停止响应而不返回(这点可以通过内部纠错机制加以保证,使之无论如何都能返回),那么线程就能顺利的被你控制了。以上是一点看法,同楼主探讨,呵呵。
使用这样的方式控制线程,不需要使用Terminat、Suspend和Resume。只需要设置IsTerm和IsWork是否为True(如果IsTerm为True,则线程主循环会做完,线程将自动退出),另外在启动的时候直接启动线程就可以了(IsTerm和IsWork会在线程创建的时候被自动赋值为False)。 ^_^
现在主要想把问题解决掉。
我程序可以通过计时器来判断线程是否处于 work 状态,但我有一点担心的是程序如果处于非工作状态,那么他可能一直sleep下去,就是不能回到工作状态。我原先在timer事件中设这么写的,
if not TGet.Suspended then //2分钟没有执行完毕,则等待它执行完毕,并不强行中止
Exit; if not TPut.Suspended then
Exit; TPut.Resume;我的这段代码思路和您得差不多,只不过是在线程外部来控制的。我用线程的suspended标志作为线程是否处于 work 状态。如果没有正常挂起(一直处于工作状态),那么就不调用线程中的代码,等到计时器的下一次事件到来。 通过实际的运行情况来看,但这种情况并没有解决我说的问题,如果出现问题后(程序没有处于suspended状态),程序一直处于挂起状态,不能定时地上传和下载文件。我怀疑是不是与 与ftp服务器打交道 的代码有问题。
如果程序处于非工作状态,我想你可以随时在主程序中通过设置IsTerm为true来结束线程。我的实际经验告诉我,系统提供的Suspended工作起来似乎不那么可靠,呵呵。
我不清楚Suspended的工作机制,但是没有检测到系统的Suspended状态,不一定就表示没有挂起,所以用它可能会导致你判断失误。如果你的问题出在这里,那么用IsWork和IsTrem就可以加以避免,因为它们都是在自己的代码中进行完全控制的。另一方面,也许你的问题在于“与ftp服务器打交道的代码”会在某种情况下实际挂起而导致线程死锁,所以我提出你大概需要在那些代码中放入某种异常机制,使得保证它们能在出错的时候也能返回。
開始運行一段時間, 線程就會挂起, 隨機的, 也找不到原因, 我也做了個與你類似的,
不過,在 TerminateThread 之後, 我加了個
Application.ProcessMessage; //實際這裹延时2秒
FreeAndNil(MyThread);
Application.ProcessMessage; //實際這裹延时2秒大概如此, 到現在, 客戶用了兩年, 也沒說出問題!我不在确定會不會內存泄露, 但當時在客戶那里開發, 沒工具測試, 後來, 沒出問題, 也就沒再理了
==========================
我加入了异常机制好的,我将按照您的想法来改造一下我的程序。
2秒这个确切数字是怎么得来的,谢谢。
不过不要担心,我肯定会揭帖的,我现在不想开新贴,否则问题又得重复描述一遍。今天我发现原先的上传线程不但没有终止掉,反而又重建了一个,相当于有两个同样的上传线程在跑。你说的办法我正想用呢(最近用的是 aiirii(ari-爱的眼睛) 的方法)。不过我有点疑问:
我必须将线程挂起,因为2秒钟执行一次。所以我觉得suspended状态和iswork有点冲突,不知道该如何安排他们,如何使用?问题解决,我另加分开贴,这个请放心。
你发出关程序就是通知程序主线程要退出了,主线程的消息循环会时刻检查这个标记的。
如果你在按钮时间下个死循环也只能杀掉。同理,你的子线程也要守点规矩。基本思路:主线程只要在必要的时候用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
or
Application. ProcessMessage
TIMER到时间再CREATE吧
suspend以后,用Resume恢复线程以后,就从suspend那个地方继续执行线程,而不是重新启动线程
因此你实际上还是在重复创建线程,而且你的线程都无法自动退出
线程创建--线程执行(ftp)那段代码--线程挂起--timer事件(这时的线程Suspened=True)--线程继续执行并自动完成(销毁)--下一个tiemr事件(这时线程已经销毁,所以Suspended=False)--用TerminateThread再强制销毁一次线程(因为你的线程已经创建过,所以这里并不报错)--接着再创建线程--接着再重复以上过程,我晕
========================================================================
是呀,这个没错因此你实际上还是在重复创建线程,而且你的线程都无法自动退出
======================================================
我假设线程如果在2分钟内正常执行完毕,他会suspend自己,就处于suspended状态,然后我在timer的事件里判断线程是否挂起(执行完毕),根据线程的suspended标志 如果执行完毕,我就resume它,并没有重复创建
如果没有suspended,我就destroy它,然后重建这个线程
==============================================
请问线程suspend,如果不唤醒它,他怎么能够继续执行呢?
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小时不停地跑。"???
1.FormCreate,线程创建
2.线程执行(这里假设ftp那一段在两分钟内未执行完毕)
3.Timer事件,Suspended=False;
执行
TerminateThread(TGet.Handle,0);
Sleep(500);
TGet:=TGetFile.Create; //创建以后并不立即执行
跳到2;