多线程下载多个任务,分批下载,及相关问题的请教

jozosoft 2011-08-17 12:02:32
一、项目需求
建立多个线程,同时对多个不同的下载任务进行下载

二、我目前的代码示例


unit main;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ExtCtrls, StdCtrls, Spin, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP, IdAntiFreezeBase, IdAntiFreeze;

type
TWeiBoThread=class(TThread)
private
// IdHttp_Sina:TIdHTTP;
fs:TStringStream;
UpdateFuncID:integer;
IsFinished:Bool;
// FURL:string ;
public
sURL,sFileSave:string;
ThreadSumCount:integer;
ThreadIndex:integer; //当前线程编号
WhatLV:TListView;

// IsBusy:Bool; //是否正在忙
//
// // IsPause_WaitDial:Bool; //主线程是否发货了暂停的命令,如果是,要挂起线程
//
// RoundIndex:integer;
LVIndex, WebIndex:integer;

constructor Create( CreateSuspended: Boolean);
destructor Destroy; override;
procedure Execute ; override;
procedure SendID(nID:string);
procedure ThreadDone( );

procedure UpdateListView();
end;


type
TfmMain = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
edtThreadCount: TSpinEdit;
Label2: TLabel;
btnInit: TButton;
Button1: TButton;
IdHTTP1: TIdHTTP;
IdAntiFreeze1: TIdAntiFreeze;
Button2: TButton;
Button3: TButton;
Button4: TButton;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
lvUserList: TListView;
mmLog: TMemo;
procedure btnInitClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
TaskSumNum,FinishedTask:Integer;
end;

var
fmMain: TfmMain;

MyThreadList: array of TWeiBoThread;

implementation

{$R *.dfm}



{ TWeiBoThread }

constructor TWeiBoThread.Create (CreateSuspended: Boolean);
begin
inherited Create(false);

LVIndex:=0;
end;

destructor TWeiBoThread.Destroy;
begin
Terminate; //TY

inherited;
end;

procedure TWeiBoThread.Execute;
var
idHttp_Sina: TIdHttp;

myStream:TStringStream;
begin
//用此句在运行过程中就报错,未知原因。2011-8-16 //●●●问题点4
//FreeOnTerminate := True; {这可以让线程执行完毕后随即释放}



if IsFinished then Exit;


myStream:= TStringStream.Create;

UpdateFuncID:=1; //1表示显示开始时间
Synchronize( UpdateListView );
//WhatLV.Items[LVIndex].SubItems[1]:= '开始:' + FormatDateTime('hh:mm:ss',now ) ;


idHttp_Sina:= TIdHttp.Create(nil);



idHttp_Sina.Get('http://www.163.com?' + IntToStr(LVIndex) , myStream );
//idHttp_Sina.Get('http://www.baidu.com?' + IntToStr(LVIndex) , myStream );

//华军软件园,不同的网页
// idHttp_Sina.Get('http://www.onlinedown.net/soft/' + IntToStr( 44800 + LVIndex) + '.htm' , myStream );



UpdateFuncID:=2; //1表示显示开始时间
Synchronize( UpdateListView );


myStream.SaveToFile( 'OutFile\' +
IntToStr(LVIndex) + '@' + FormatDateTime('hhmmss',now ) + '.txt'
) ;

myStream.Free;
idHttp_Sina.Free;

Sleep(100);

IsFinished:=True;

inc( fmMain.FinishedTask );

if fmMain.FinishedTask = fmMain.TaskSumNum then
begin

fmMain.mmLog.Lines.Add('多线程完成时间:' + FormatDateTime('hh:mm:ss',now ) );
ShowMessage('所有任务都已完成!');
end;


inherited;
end;

procedure TWeiBoThread.SendID(nID: string);
begin
//
end;

procedure TWeiBoThread.ThreadDone;
begin
exit;

WhatLV.Items[LVIndex].SubItems[1]:=
WhatLV.Items[LVIndex].SubItems[1] + '...ThreadDone:完成:' + FormatDateTime('hh:mm:ss',now ) ;
end;


procedure TWeiBoThread.UpdateListView;
begin
if UpdateFuncID=1 then
WhatLV.Items[LVIndex].SubItems[1]:= inttostr(LVIndex) + ' 开始:' + FormatDateTime('hh:mm:ss',now )
else if UpdateFuncID=2 then
begin
WhatLV.Items[LVIndex].SubItems[1]:=
WhatLV.Items[LVIndex].SubItems[1] + '...完成:' + FormatDateTime('hh:mm:ss',now ) ;
end;
//Synchronize()的作用? 多线程之间控制同步过程.比方说要访问一种资源时,避免同时操作.

//synchronize是不可以带参数的。
end;

//////////////////////

procedure TfmMain.Button1Click(Sender: TObject); //建立线程
var
i:integer;
begin
Caption:= '多线程...';

TaskSumNum:= edtThreadCount.Value;
FinishedTask:=0;

SetLength(MyThreadList, edtThreadCount.Value +1 ); //线程数量


mmLog.Lines.Add('多线程开始时间:' + FormatDateTime('hh:mm:ss',now ) );

for I := 1 to edtThreadCount.Value do
begin
MyThreadList[i] := TWeiBoThread.Create( false );
MyThreadList[i].LVIndex:= i-1;
MyThreadList[i].WhatLV:= lvUserList;

// MyThreadList[i].FreeOnTerminate:=True;
// MyThreadList[i].OnTerminate:= MyThreadList[i].ThreadDone; //
end;

for I := 1 to edtThreadCount.Value do
begin
//MyThreadList[i].Resume;
// MyThreadList[i].Start;
MyThreadList[i].Execute;
end;

mmLog.Lines.Add('多线程呼叫完成,等待线程们处理,呼叫时间:' + FormatDateTime('hh:mm:ss',now ) );

end;

procedure TfmMain.Button3Click(Sender: TObject); //挂起线程 //●●●问题点3
var
i:integer;
begin
for I := 1 to edtThreadCount.Value do
begin
// MyThreadList[i].Suspend;
if MyThreadList[i].Terminated=False then
MyThreadList[i].Suspended:= True;

end;
end;

procedure TfmMain.Button4Click(Sender: TObject); //继续线程
var
i:integer;
begin
for I := 1 to edtThreadCount.Value do
begin
MyThreadList[i].Resume; // .Suspend; //如果线程已终止,则无法暂停止


end;
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
btnInit.Click;
end;

procedure TfmMain.btnInitClick(Sender: TObject);
var
nIndex,i:integer;
begin
lvUserList.Items.Clear;
nIndex:=0;
for i := 0 to 1000 - 1 do
begin //产生1000个数字序列

inc(nIndex);
with lvUserList.Items.Add do
begin
Caption:= inttostr(nIndex);
SubItems.Add( 'Num' + inttostr(nIndex) ) ;

SubItems.Add( '' ) ;

end;


end;
end;


end.



------------------------

三、问题点
1、上述代码,为什么我的线程任务全部完成后,在ListView中,开头的1-2个任务还会重新执行一次?
2、假设我现在有1000个任务要下载,然后我建立了10个线程,是不是就是在 Execute 中,放一个For 循环,如下:


procedure TWeiBoThread.Execute;
....
Begin
For i:=1 to 任务总数 / 线程数 + 1
Begin
if i >= WhatLV.Items.Count then Exit;

//这里面写原来的那些Execute语句体
End
End;


是这样重复去下载吗?



3、问题3
以问题2为例,我现在假设下载完成 XX 个任务时,要重新拨号,那么,我要怎么挂起任务,然后等拨号成功后再继续执行?
我现在的方法是想在主程序中先检测是否需要重新拨号,如果是,就执行上面的 //●●●问题点3
3.1 但我发现假如某个线程正在执行中,它好像根本不暂停或挂起。
3.2 如果说是一个线程必须要execute完毕才能挂起,那我拨号完后,怎么让它从下一个循环继续开始?是自己去控制 For 语句的开始数值,还是线程有其它方法让程序继续?

3.3 我们挂起一个线程,再继续一个线程时,它是重新执行 Execute ,还是从原来线程没有完成的地方继续执行?
3.4 在线程们执行过程中,我要强行退出程序,又要怎么做?我发现好像程序都关闭i不了,非得全部执行完。


4、问题点4

//用此句在运行过程中就报错,未知原因。2011-8-16 //●●●问题点4
//FreeOnTerminate := True; {这可以让线程执行完毕后随即释放}

上述一行代码,我为什么一加上它,程序运行就会报内存错?


以上诸个问题,诚请过来人和大虾们指教!
谢谢!
...全文
172 3 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
Mit1208 2011-08-17
  • 打赏
  • 举报
回复
代码太多,不细看了,直接回复你的相关问题!

1.对于这个问题,应该是你没有加入同步开始数值的问题。每次开始都是从起初开始执行的!
2.使用while not terminated do这样的去判断是否终止吧,里边加入你想跳出循环的一个判断语句
3.1应该是你的代码有问题吧,应该可以挂起的。
3.2拨号完后直接使用唤醒就行了
3.3从原来线程没有完成的地方执行
3.4如果是线程的话,怎么会关闭不了呢?是不是线程没有用对?关闭线程可以使用TThread.Terminate;然后TThread.Free;释放线程。
4.如果使用Freeonterminate的话就不要使用TThread.Free这样的了,会提示访问句柄错误的。
如果在线程执行的时候想关闭程序,就先判断一下线程是否正在运行,如果正在运行则TThread.Terminate,然后再TThread.Free;
无条件为你 2011-08-17
  • 打赏
  • 举报
回复
把你的大虾改为大侠,或者读者更愿意帮你。
rainychan2009 2011-08-17
  • 打赏
  • 举报
回复
针对4,个人不建议FreeOnTerminate := True;手动释放更方便。

5,929

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧