在TThread中有一个Terminated的属性,你可以用它来中止线程。 一般来说,用线程都是工作线程,应该都有一个循环体例如 procedure WordThread() begin .... while (not FEnd) do begin .... if self.Terminated then break; .... end; ..... end;procedure Button.Click() begin AThread.Terminated := true; end;
To zeroxing(胸无半点墨,腰有万贯财;此般理想高,怎奈做不到!--) : 对于你的第一个提议,那是我的笔误。第二个,我是希望在我按下另一个按钮后,正运行的线程能停止运行,也许用挂起会更好。第三个,如果我用了Synchronize()方法,在一个线程执行时,我就没法让用户结束或挂起它,这样与用单线程没有操作上的区别。第四个,我需要多线程并非因为耗费时间的循环,而是因为耗费时间的下载过程,客户的服务器是无线上网(我们提过用专线,但无效),客户端用ftp下载很多文件,因为速度慢所以等待时间长且程序位单线程程序,所以下载执行不完界面就不响应用户了(死了)。第五,我真的没写过线程程序,所以有很多地方做得不好,我接受批评。第六,对于起名,那只不过是我的试验程序,当然我还是要接收你的建议应该让名字有意义。 虽然一直没有好的方法可以解决我的问题,但我还是感谢各位。
To zeroxing(胸无半点墨,腰有万贯财;此般理想高,怎奈做不到!): 按你的建议我试了一下,有个问题,请见程序代码,简单写了: procedure Tdd.execute; begin inherited; self.OnTerminate:=form1.termdd; self.prog; end;procedure Tdd.moveprogbar; begin form1.ProgressBar1.StepIt; end;procedure Tdd.prog; var i,j:integer; begin for j:=0 to 2000 do begin if self.Terminated=false then begin for i:=0 to 2000000 do;//代表进行的一些数据库操作或传数据 synchronize(self.moveprogbar); FrmMain.connection:=true; end else break; end; end; 1.但在线程运行中,如果我点一按钮terminate它,不起作用,suspend它则起作用。 2.如果在它运行的时候,移动了一下form1,那么form1界面移到位置后就不再立即响应用户操作了,很奇怪。 3.还有就是进度条走完它执行了form1.termdd,但它的terminated还是为false,只有在进度条走完并显示调用terminate后terminated才为true。 4.“线程里面跑FTP,我做过的。”ftp主键是在线程里生成的吗,怎么做的?如果是,传输时ftp事件你又是怎么写的?如果不是,那么ftp组建仍是VCL,这么用也要对ftp组件的传输过程加synchronize?
但好象用synchronize()同步方法,可以实现的要的功能吧?
在线程中Execute中调用的方法里面检测IsStop 的值
if IsStop then
begin
Abort; //或者EXIT;
end;
如:SendMessage(AThread1.Handle, WM_USER + 1000, 0, 0);
在线程AThread1中处理WM_USER + 1000这个消息
一般来说,用线程都是工作线程,应该都有一个循环体例如
procedure WordThread()
begin
....
while (not FEnd) do
begin
....
if self.Terminated then break;
....
end;
.....
end;procedure Button.Click()
begin
AThread.Terminated := true;
end;
如果运用synchronize()方法,那么应该怎么做?
类似这样的问题:有一个进度条,先在窗体的方法中进行初始化,然后用线程的方法操作进度条,这样的话它会抱报错。最后关掉窗体时,报一个“句柄错误”。是不是操作组件的方法都要写在线程中,是不是需要在线程类中设计组件类的变量,用于初始化时类似FProg=form1.progressbar;这样。
请各位指点。
1、在线程里面调用Synchronize是迫不得已的时候才会做的,因为调用该方法后,线程将附着在主线程里运行,也就是说多线程此时已经退化成为单线程;
2、我们都知道,为了线程安全,从不同线程里面访问VCL可视组件是需要调用Synchronize方法的。但是,一定要把这种情况尽最大可能地减少。如果你的一个线程里,绝大多数甚至全部的时间都是在调用Synchronize方法,那么根据上面第一点,你相当于用多线程来实现单线程。从成本收益来说,这是不划算的,你还不如不用多线程。
3、基于第二点,并且假设你的设计是符合成本收益最大化原则的,那么,你的线程里面肯定只有少数时间在用Synchronize访问VCL控件,而大多数时间在做其他的不涉及Synchronize的操作。例如,你可能在一个循环里面做排序的操作,每次循环的最后一步调用Synchronize显示一下结果,然后再次脱离主线程进入下一次循环。
4、这样,在你的线程执行过程中,主线程就有机会去响应用户的操作,而不会出现线程执行后不响应用户操作的情况。此时,用按钮事件和线程的Terminated属性,就可以轻易地达成你的目的。但是,我高度怀疑,你把整个线程的Execute都放在Synchronize里面执行了。如果真的是这样,我认为你应该从整体上修改一下你的设计。
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;type
Tdd=class(tthread)
protected
procedure execute;override;
procedure sss;
procedure aaa;
procedure bbb;
procedure InitProgBar(const stepcount:integer;const curpos:integer=0);
FProgRect:trect; //进度条组件尺寸
end;
TForm1 = class(TForm)
Button1: TButton; Button2: TButton;
StatusBar1: TStatusBar;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure StatusBar1DrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
private
{ Private declarations }
dd:tdd;
ProgressBar2: TProgressBar; //进度条组件
ProgressBarRect:trect; //进度条组件尺寸
procedure ss(sender:tobject);
procedure InitProgBar(const stepcount:integer;const curpos:integer=0);
//初始化进度条
public
{ Public declarations }
end;var
Form1: TForm1;implementation
uses mainfrm;
{$R *.dfm}{ Tdd }procedure Tdd.aaa;
begin
showmessage('aaa');
self.bbb;
end;procedure Tdd.bbb;
begin
showmessage('bbb');
self.sss;
end;procedure Tdd.execute;
begin
inherited;
//self.FreeOnTerminate:=true;
self.OnTerminate:=form1.ss;
self.aaa;
end;procedure Tdd.sss;
var i:integer;
begin
form1.InitProgBar(2000,0);
while form1.ProgressBar2.Position<form1.ProgressBar2.Max do
begin
if self.Terminated=false then
begin
for i:=0 to 200000 do;
form1.ProgressBar2.StepIt;
FrmMain.connection:=true;
end
else break;
end;
form1.ProgressBar2.Free;
end;procedure TForm1.Button1Click(Sender: TObject);
begin
dd:=Tdd.Create(form1);
end;procedure TForm1.ss(sender: tobject);
begin
self.ProgressBar2.Position:=0;
end;procedure TForm1.Button2Click(Sender: TObject);
begin
dd.Terminate;
end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if dd<>nil then
begin
dd.Terminate;
dd.Free;
end;
action:=cafree;
end;procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
begin
ProgressBarRect:= Rect;
end;procedure TForm1.InitProgBar(const stepcount, curpos: integer);
begin
self.ProgressBar2:= tprogressbar.Create(self);
with ProgressBar2 do
begin
top:= ProgressBarrect.Top; left:= ProgressBarrect.Left;
width:= ProgressBarrect.Right-ProgressBarrect.Left;
height:= ProgressBarrect.Bottom-ProgressBarrect.Top;
visible:=true; parent:=self.StatusBar1;
min:=0; max:=stepcount;
step:=1; Position:=curpos;
end;
end;end.
1、dd:=Tdd.Create(form1);
这个能编译过去?没有看到你重载了Thread的Create;2、来看看你的线程起来后,一路走下来的历程:
excute -> self.aaa -> self.bbb -> self.sss -> form1.InitProgBar(2000,0) -> ... -> form1.ProgressBar2.Free
好,到了这里,你的form1里面的PogressBar2已经free掉了,不存在了。
然后接下来你知道会怎么样吗?
因为你前面指定了self.OnTerminate:=form1.ss;所以,现成就会调用Form1.ss。再看看你在form1.ss里面做了什么:ProgressBar2.Position:=0;
能不出错才真正怪了,ProgressBar2早就已经free掉了,你还要调用它的属性!3、你在线程里面所有对form1上面的控件(例如ProgressBar2)进行的操作,都没有调用Synchronize,这是严重的错误;4、在线程里面调用Application.postMessage?正常的情况不会出现如此奇怪的组合,线程用得好的话,根本就不可能产生主界面死掉的情况(就算你的线程死掉了,主界面也不会死),也不必要调用PostMessage去强制处理消息!除非你用的是单线程,才会需要在做很耗时间的循环时,偷空PostMessage一下,不让主界面死掉。5、结构太混乱了,应该在form1的东西,出现在线程里面,使得一切就向面条一样的缠绕着。写的好的程序,结构是非常美丽和清晰的,也是非常的简单的。你能用面向对象的东西,作出以前用Goto作出的效果,真的不知道赞美还是贬低!6、变量、过程函数命名一塌糊涂,dd是什么?ss是什么?sss是什么?想让人看,还是不想让人看?楼主,建议你:
在这个往往充斥着某种语言好或者坏的极端的争论的世界里面,要安静下来,多学学基础的东西。你要相信,当你在思维里面已经把一切都理顺了,不用语言,就用平常生活油盐酱醋的比喻,用公共汽车排队来比喻,用分工协作来比喻,用这些来帮助你理解什么叫做流程,一切搞清楚了,你要相信,用什么语言来实现,已经是非常简单的了。不要学着人家,拿一本破书照着抄写,然后就以为一切都可以了!你忽视了最重要的东西,你不去搞懂这个最重要的东西,反而去弄皮毛的东西。你不搞懂线程是什么东东,你写出来的东西,能不奇怪么?这是一个公司聚餐后的夜晚,红酒的作用充斥在我的身体,直到我写完上面的这些。不要见怪,我并没有恶意。
面条没有煮过的时候,是一根根的,条理很清晰^_^
但我想我没有误会楼主的意思,楼主指的也就是ProcessMessages吧,否则Application.PostMessage,Application看来是没有这个方法的。
对于你的第一个提议,那是我的笔误。第二个,我是希望在我按下另一个按钮后,正运行的线程能停止运行,也许用挂起会更好。第三个,如果我用了Synchronize()方法,在一个线程执行时,我就没法让用户结束或挂起它,这样与用单线程没有操作上的区别。第四个,我需要多线程并非因为耗费时间的循环,而是因为耗费时间的下载过程,客户的服务器是无线上网(我们提过用专线,但无效),客户端用ftp下载很多文件,因为速度慢所以等待时间长且程序位单线程程序,所以下载执行不完界面就不响应用户了(死了)。第五,我真的没写过线程程序,所以有很多地方做得不好,我接受批评。第六,对于起名,那只不过是我的试验程序,当然我还是要接收你的建议应该让名字有意义。
虽然一直没有好的方法可以解决我的问题,但我还是感谢各位。
按你的建议我试了一下,有个问题,请见程序代码,简单写了:
procedure Tdd.execute;
begin
inherited;
self.OnTerminate:=form1.termdd;
self.prog;
end;procedure Tdd.moveprogbar;
begin
form1.ProgressBar1.StepIt;
end;procedure Tdd.prog;
var i,j:integer;
begin
for j:=0 to 2000 do
begin
if self.Terminated=false then
begin
for i:=0 to 2000000 do;//代表进行的一些数据库操作或传数据
synchronize(self.moveprogbar);
FrmMain.connection:=true;
end
else break;
end;
end;
1.但在线程运行中,如果我点一按钮terminate它,不起作用,suspend它则起作用。
2.如果在它运行的时候,移动了一下form1,那么form1界面移到位置后就不再立即响应用户操作了,很奇怪。
3.还有就是进度条走完它执行了form1.termdd,但它的terminated还是为false,只有在进度条走完并显示调用terminate后terminated才为true。
4.“线程里面跑FTP,我做过的。”ftp主键是在线程里生成的吗,怎么做的?如果是,传输时ftp事件你又是怎么写的?如果不是,那么ftp组建仍是VCL,这么用也要对ftp组件的传输过程加synchronize?
=================
terminate 之前最好把运行中的线各挂起
FrmMain.connection是什么东西?
1里面的按钮响应事件是怎么写的?3,terminated除非你显性调用terminate或者terminated := true,否则它不会自己成为false的,即便线程跑完了,它的作用就仅仅是提供用户一个变量用来控制excute里面循环的退出,你也可以不用它,而定义自己的一个boolean变量,例如myTerminated这样。4,Ftp组件是在线程里面动态生成的,所以不涉及synchronize的问题(其实上,这种不可视的组件一般来说是线程安全的)。
begin
//往主窗体写信息。现写的,不是我原来代码的。
FrmMain.mmLog.Lines.Add(FTaskStatus);
end;//FTP的事件响应过程。设定看Excute里面。
procedure TUploadTask.OnIdFTPWork(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
var
fPercent: double;
fFileSize: double;
fWorkCount: double;
begin
if FCurFileSize <> 0 then
fPercent := (AWorkCount / FCurFileSize) * 100
else
fPercent := 0.0; fFileSize := FCurFileSize;
fWorkCount := AWorkCount;
FTaskStatus := Format('进度:%.0n字节,总共:%.0n字节,完成率%3.3f%s',
[fWorkCount, fFileSize, fPercent, '%']);
FTaskEventType := tetProgress;
self.Synchronize(fireTaskEvent);
end;
procedure TUploadTask.execute;
var
IdFtp: TIdFtp;
i: integer;
begin
IdFtp := TIdFtp.Create(nil);
IdFtp.OnStatus := OnIdFTPStatus;
IdFtp.OnWork := OnIdFtpWork;//////////事件是这么设定的。
try
try
with IdFtp do
begin
TransferType := ftBinary;
Host := FFtpHost;
Port := FFtpPort;
Passive := FPassiveMode;
User := FFtpUser;
Password := FFtpPwd; //连接服务器。
Connect(true);
//转到工作目录。
ChangeDir(FFtpPath);
FTaskStatus := '成功登录到FTP服务器,当前目录:' + retrieveCurrentDir;
self.Synchronize(fireTaskNormalMessage); FTotalFileNum := FFilesList.Count;
FTaskStatus := Format('总共%d个文件',[FTotalFileNum]);
self.Synchronize(fireTaskNormalMessage);
FCurFileSeq := 0;
for i := 0 to FTotalFileNum - 1 do
begin
FCurFileSeq := i + 1;
FCurFileName := FFilesList.Strings[i];
FTaskStatus := Format('第%d个文件:%s', [FCurFileSeq, FCurFileName]);
self.Synchronize(fireTaskNormalMessage);
Put(FCurFileName, ExtractFileName(FCurFileName));
end;
FEndSuccess := true;
end;
except
on E: Exception do
begin
FTaskStatus := '发生错误,任务停止执行。原因:' + E.Message;
FEndSuccess := false;
self.Synchronize(fireTaskNormalMessage);
end;
end;
finally
IdFtp.Disconnect;
IdFtp.Free;
end;
end;