PDA

Ver la Versión Completa : Multithread


angelp4492
11-05-2012, 10:40:23
Buenos días, siguiendo un ejemplo de downloader de Neftali me he creado esta unidad de descarga



unit UnitDownload;

interface
uses
Dialogs,SysUtils,Classes, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,DateUtils;

type
TIdHTTPThread = class(TThread) //DEFINIMOS EL thread de decarga
private
//definimos las variables del thread
FURL: AnsiString; //dirección de descarga
FFilename: AnsiString; //nombre del fichero de descarga
velocidad,transferencia,tiempo,size,porcentaje: string; // velocidad,transferencia de descarga y tamaño del fichero
posProgress,maxProgress : Int64; // datos para la barra de progreso
Proxyport: Int64; //datos para el proxy
ProxyServer:AnsiString;
IdHTTP: TIdHTTP; //objeto TidHTTP
ProxySI : Boolean; // para comprobar si la conexión es por proxy
threadterminado : Boolean; //para esperar que termine el thread
parar : Boolean; //parar el thread.
TID : Integer; //identificador del thread
//definimos los procedimientos del thread
procedure OnWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
procedure OnWorkBegin(Sender: TObject; AWorkMode: TWorkMode;const AWorkCountMax: Integer);
procedure OnWorkEnd(Sender: TObject; AWorkMode: TWorkMode);
procedure cancel;
procedure Updatelabel;
procedure UpdaProgressbar;
procedure FinThread;
procedure IniThread;
procedure EndNotify;
public
//constructor del thread para pasarle parámetros.
constructor Create(sFUrl : AnsiString; destino : AnsiString; Proxy :Boolean; sProxyport : Int64; sProxyserver:AnsiString ; sTID:Integer);
Destructor Destroy; override;
//property Url: AnsiString read FURL write FUrl;
//property Filename: AnsiString read FFilename write FFilename;
//property Proxy_SI : Boolean read ProxySI write ProxySI;
//property Proxy_Port: Int64 read Proxyport write Proxyport;
//property Proxy_Server: AnsiString read ProxyServer write ProxyServer;
property terminado :Boolean read Threadterminado write Threadterminado;
// con la propiedad property intercambio información con el thread y el formulario
property parate : Boolean read parar write parar;
protected
procedure Execute; override;
end;

implementation
uses Unit1; // aqui ponemos la unidad desde donde será ejecutado el thread.

constructor TIdHTTPThread.Create(sFUrl : AnsiString; destino : AnsiString; Proxy :Boolean; sProxyport : Int64; sProxyserver:AnsiString ; sTID:integer);
begin
inherited Create(false);
FreeOnTerminate := True;
FURL :=sFUrl;
FFilename:=destino;
ProxySI:=Proxy;
Proxyport:=sProxyport;
ProxyServer:=sProxyserver;
TID:=sTID



end;

destructor TIdHTTPThread.Destroy;
begin
IdHTTP.Free;

inherited;
end;
procedure TIdHTTPThread.cancel;
begin
if (IdHTTP.Connected) and (parar = True) then
IdHTTP.Disconnect;
end;
procedure TIdHTTPThread.Execute;
var
fs: TFileStream;
idHTTP : TidHttp;
begin
Synchronize(IniThread);
idHttp:= Tidhttp.create(nil);
if ProxySI = true then
begin
IdHTTP.ProxyParams.ProxyServer:=ProxyServer;
IdHTTP.ProxyParams.ProxyPort:=Proxyport;
end;
idHTTP.HandleRedirects := True;
idHTTP.ReadTimeout := 30000;
IdHTTP.ProtocolVersion:= pv1_0;
idHTTP.Request.UserAgent := 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0)';
IdHTTP.OnWork := Onwork;
IdHTTP.OnWorkBegin:=OnWorkBegin;
IdHTTP.OnWorKEnd:=OnWorkEnd;
//ShowMessage('proxy='+ProxyServer);
//ShowMessage(FFilename);
//ShowMessage(furl);
fs := TFileStream.Create(FFilename, fmCreate);
try
IdHTTP.Get(FUrl, fs);

finally
fs.Free;


Synchronize(EndNotify);
end;

end;



//Poner estado de valores cuando se empieza a descargar
procedure TIdHTTPThread.OnWorkBegin(Sender: TObject; AWorkMode: TWorkMode;const AWorkCountMax: Integer);
begin
posProgress:= 0;
if AWorkMode = wmRead then //uniquement quand le composant recoit des données
begin
maxProgress := AWorkCountMax; //Maximum = taille du soft à télécharger

posProgress := 0; //Position à zéro
end;
if AWorkMode = wmRead then //uniquement quand le composant recoit des données
begin
velocidad := 'Descarga: 0 Kb/s ';
FStartDate := Now; //enregistre la date
//Application.ProcessMessages; //Evite que l'appli soit "gelée"
end;
Synchronize(Updatelabel);
Synchronize(Updaprogressbar);
end;




procedure TIdHTTPThread.OnWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
var
ElapsedTime : Cardinal;
n1,n2 : double;
begin
// Avanzando...

if AWorkMode=wmRead then //quand on recoit des données
begin

posProgress := AWorkCount + 40;
size := Format(' %s Mb',[FormatFloat('0.00',(maxProgress)/1024000)]);
ElapsedTime := SecondsBetween(Now,FStartDate); //Calcule la vitesse de téléchargement
if ElapsedTime>0 then
begin
velocidad := Format('Descarga: %s Kb/s',[FormatFloat('0.00',(AWorkCount/1024)/ElapsedTime)]);
n1:= (AWorkCount/1024000);
n2:= (maxProgress/1024000);
// velocidad := Format('Descarga: %s Kb/s',[FormatFloat('0.00',(AWorkCount*elapsedtime)/1024)]);

transferencia:= Format('Descargado: %s Mb ',[FormatFloat('0.00',(AWorkCount/1024000))])+Format('De: %s Mb',[FormatFloat('0.00',(maxProgress)/1024000)]);;
//lbldescarga.Caption:=lbldescarga.Caption+' de '+ Format('Descargado: %s Mb/s',[FormatFloat('0.00',(ProgressBar1.Max)/1024000)]);

porcentaje:=Format('Porcentaje: %s',[FormatFloat('0',((AWorkCount*100)/maxprogress))])+'%';

tiempo:=Format('Tiempo Restante %s ',[FormatDateTime('tt',((n2-n1)*elapsedtime))]);
//Application.ProcessMessages; //Evite que l'appli soi "givrée"

end;

end;
//ShowMessage(IntToStr(aworkcount));
Synchronize(Updatelabel);
Synchronize(Updaprogressbar);
end;
procedure TIdHTTPThread.OnWorkEnd(Sender: TObject; AWorkMode: TWorkMode);
begin
// Acabado
maxProgress := 0;
transferencia:='Descargando:';
velocidad:='Descarga:';
Synchronize(Updatelabel);
Synchronize(Updaprogressbar);
end;
//procedimiento para actualizar las variables del form
//los llamamos con synchronize (nameprocedimiento=
procedure TIdHTTPThread.Updatelabel;
begin
Form1.UpdateLabel(velocidad,transferencia,tiempo,size,porcentaje);
end;

procedure TIdHTTPThread.UpdaProgressbar;
begin
Form1.UpdateProgressbar(posProgress,maxProgress);
end;
procedure TIdHTTPThread.FinThread ;
begin
form1.finthread:=True;
end;
procedure TIdHTTPThread.IniThread ;
begin
form1.finthread:=false;
end;

procedure TIdHTTPThread.EndNotify;
begin
Form1.eventhreadterminate(TID);
end;

end.




seguramente se podrá mejorar.
El problema es que esto me funciona cuando tengo un solo thread, pero como haria por ejemplo que es lo que quiero hacer para mandar los datos de descarga con un progressbar a un listview y en multithread. O sea un Multidownloader con listview y progressbar al estilo del Jdonwloader.

Casimiro Notevi
11-05-2012, 15:58:23
Recuerda poner títulos descriptivos a las preguntas, gracias :)

javier7ar
11-05-2012, 18:03:30
pero si eso que tenes ya es multithread! cada instancia que creas de esta clase es un thread; si creas 5 instancias vas a tener 5 threads!
lo unico que te faltaria es tener 5 barras de progreso, y alguna forma de decir que barra corresponde a que thread.
Cuando digo 5, pueden ser los que quieras
Si no es eso a lo que te referis, escribi un ejemplo de lo que necesitas, asi te podemos ayudar mejor
Saludos

angelp4492
11-05-2012, 18:27:26
pero si eso que tenes ya es multithread! cada instancia que creas de esta clase es un thread; si creas 5 instancias vas a tener 5 threads!
lo unico que te faltaria es tener 5 barras de progreso, y alguna forma de decir que barra corresponde a que thread.
Cuando digo 5, pueden ser los que quieras
Si no es eso a lo que te referis, escribi un ejemplo de lo que necesitas, asi te podemos ayudar mejor
Saludos


Si a eso exactamente me refiero, tengo una barra de progreso y cuando lanzo 5 thread todos me muestran los datos en la misma barra, y no se como poner los datos en sus correspondientes barras.

javier7ar
14-05-2012, 13:43:53
y tendrias que identificar cada thread, por ejemplo mandandole un nro cuando lo creas, entonces despues cuando el thread llama a actualizar el progressbar pasa su nro por parametro y el Form1 en base al nro que recibe actualiza uno u otro progressbar.
Seria algo asi:

constructor TIdHTTPThread.Create(sFUrl : AnsiString; destino : AnsiString; Proxy :Boolean; sProxyport : Int64; sProxyserver:AnsiString ; sTID:integer; Nro:Integer);
begin
inherited Create(false);
FreeOnTerminate := True;
FNro:=Nro;
...

y cuando llamas a actualizar la barra de progreso:

// y aca cambias esto:
Form1.UpdateProgressbar(posProgress,maxProgress);
// por esto:
Form1.UpdateProgressbar(posProgress,maxProgress,FNro);

y en el procedimiento UpdateProgressbar te fijas en el nro que te pasan por parametro y actualizas la barra que corresponde
Espero se entienda
Saludos

angelp4492
15-05-2012, 09:09:47
Siguiendo unos ejemplos que hay por la red y cambiando la forma que el thread manda información, usando WM_USER, me he creado una clase para controlor el thread.

En la recepción del mensaje del thread tengo esto.


Procedure Tform1.HTTPClientDownloadBeginWork(var Msg:TMessage);
var
uniqueid_: integer;
totalbytes_: integer;
i: integer;
begin
uniqueid_ := Msg.wparam;
totalbytes_ := Msg.LParam;

i := IndexOfDownloadableFile(uniqueid_);
if i <> -1 then
begin

//ShowMessage(IntToStr(totalbytes_));
form1.ProgressBar1.Position:=0;
form1.ProgressBar1.Max:=totalbytes_;
TTransferFile(DownloadList[i]).BytesDownloaded := TTransferFile(DownloadList[i]).BytesPreviouslyDownloaded;
TTransferFile(DownloadList[i]).TotalFileSize := TTransferFile(DownloadList[i]).BytesPreviouslyDownloaded + totalbytes_;
//TDownloadFile(DownloadaList[i]).State := si_Downloading_Animation1;
TTransferFile(DownloadList[i]).ProgressBar.Position := 0;
TTransferFile(DownloadList[i]).ProgressBar.Max := TTransferFile(downloadlist[i]).TotalFileSize;
ListviewTransfer.Repaint;

end;
end;


donde TTransferfile y Tdownload file son clases para manejar la descarga y downloadlist una variable de tipo objectlist. el progressbar lo creo de esta manera.



procedure TTransferFile.addToView;
var
RectProg: TRect;
begin
item := ListView.items.add;
item.Caption := Url;
Item.SubItems.Add('0 kb/s');
Item.SubItems.Add('');


//if es_descarga then Item.ImageIndex := 9
//else Item.ImageIndex := 10;
ProgressBar := TsProgressBar.Create(nil);
Item.Data := ProgressBar;
RectProg := Item.DisplayRect(drBounds);
RectProg.Left := RectProg.Left + ListView.Columns[0].Width;
RectProg.Right := RectProg.Left + ListView.Columns[1].Width;
ProgressBar.BoundsRect := RectProg;
progressBar.Parent := ListView;

end;
.

En totalbytes_ tengo el tamaño del fichero en bytes, el progressbar1 componente normal me lo acepta si problemas cuando pongo form1.progressbar1.max=totalbytes_

El problema que tengo es en esta linea

TTransferFile(DownloadList[i]).ProgressBar.Max := TTransferFile(downloadlist[i]).TotalFileSize;

me sale un error que solo acepta entre 0 y 65536. Alguna Idea.