Poslao: 08 Feb 2006 12:18
|
offline
- bobby
- Administrator
- Pridružio: 04 Sep 2003
- Poruke: 24135
- Gde živiš: Wien
|
Aplikacija ucitava u StringGrid listu URL adresa koje trebaju da se otvore u IE-u. Posto lista sadrzi vise hiljada elemenata, zelim da mogu da izvrsavam procesiranje u paralelnim threadovima (posto dobar deo procesiranje odlazi na cekanje, tj. ucitavanje sajta).
E sada, nije mi problem da uradim pet threadova koji ce paralelno da se izvrsavaju. Fora je da zelim da se u aplikaciji moze podesiti koliko threadova paralelno da se izvrsavaju.
Znaci, threadove bih kreirao u nekoj petlji. Svakom bih dodelio neki ID (u vidu globalne promenljive u threadu). Problem mi je sto neznam kako mogu da uradim da thread javlja kada je zavrsio, da bi aplikacija tom threadu poslala sledeci elemenat za procesiranje.
Fali mi znaci sledece:
- kako da glavna forma ceka na poruke iz threadova
- kako thread da signalizira kraj procesiranja
Nemojte samo da mi kazete da treba da uradim neki Message Handler, i da zavrsite na tome. Nikada to nisam radio, niti znam gde da potrazim uputstva. Treba mi dakle konkretan kod (code) koji treba da ubacim u aplikaciju da bi se uradilo ovako nesto.
Jedini primer koji sam nasao na netu je ovaj:
http://community.borland.com/article/0,1410,22411,00.html
ali nije bas da ga razumem.
Unit u kome je thread:
unit ProcThread;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Process;
type
thProcThread = class(TThread)
private
procedure UpdateStatus;
{ Private declarations }
protected
procedure Execute; override;
public
procedure SetURL (URLstring: string);
procedure SetID (ID: integer);
procedure SetTime (Time: integer);
end;
implementation
uses untMain;
var
ID_local: integer;
URL_local: string;
Time_local: integer;
procedure thProcThread.Execute;
var
AProcess: TProcess;
begin
AProcess := TProcess.Create(nil);
AProcess.CommandLine := '"C:\Program Files\Internet Explorer\IEXPLORE.EXE" ' + URL_local;
AProcess.Execute;
sleep(Time_local * 1000);
AProcess.Terminate(0);
AProcess.Free;
Synchronize (@UpdateStatus);
end;
procedure thProcThread.UpdateStatus;
begin
end;
procedure thProcThread.SetID(ID: integer);
begin
ID_local := ID;
end;
procedure thProcThread.SetURL(URLstring: string);
begin
URL_local := URLstring;
end;
procedure thProcThread.SetTime(Time: integer);
begin
Time_local := Time; //vreme posle koga ce IE biti ubijen
end;
end.
Deo koda u glavnom unitu koji otvara jedan thread:
procedure TfrmMain.btnProcessClick(Sender: TObject);
var
PrThread: thProcThread;
begin
PrThread := thProcThread.Create(true);
PrThread.SetURL('google.com');
PrThread.SetID(1);
PrThread.SetTime(SpinEdit2.Value);
PrThread.FreeOnTerminate := true;
PrThread.Resume;
end;
Ovde ce umesto fiksne adrese (google.com) ici StringGrid1.Cells[0, counter], cime bi se URL ucitavao iz liste.
Jezik je FreePascal. TProcess je multiplatformski unit u fazonu ShellExec, s tim sto poseduje i Pipes (sto u gornjem primeru nije korisceno).
|
|
|
Registruj se da bi učestvovao u diskusiji. Registrovanim korisnicima se NE prikazuju reklame unutar poruka.
|
|
|
Poslao: 09 Feb 2006 14:58
|
offline
- bobby
- Administrator
- Pridružio: 04 Sep 2003
- Poruke: 24135
- Gde živiš: Wien
|
Evo kako sada izgleda code (posto Freepascal nema .Terminated property za TThread):
U Mainu
procedure TfrmMain.btnProcessClick(Sender: TObject);
var
MyRelayThread: thRelayThread;
Counter: integer;
I: integer;
begin
MyRelayThread := thRelayThread.Create(true);
MyRelayThread.SetThreadCount(SpinEdit1.Value);
MyRelayThread.SetListCount(StringGrid1.RowCount);
MyRelayThread.SetLastLine(lastLine); //linija kod koje se stalo u prethodnoj sesiji, da ne bi pocinjao od pocetka
MyRelayThread.SetTime(SpinEdit2.Value);
MyRelayThread.SetGrid(StringGrid1);
MyRelayThread.FreeOnTerminate := true;
MyRelayThread.Resume;
application.ProcessMessages;
if CancelFlag = true then
begin
MyRelayThread.Terminate;
cancelFlag := false;
end;
end;
Relay Thread (koji otvara dalje threadove i ceka da se zatvore, a da ne blokira glavnu aplikaciju)
unit RelayThread;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Grids;
type
thRelayThread = class(TThread)
private
procedure UpdateStatus;
{ Private declarations }
protected
procedure Execute; override;
public
procedure SetThreadCount (ThreadCount: integer);
procedure SetListCount (ListCount: integer);
procedure SetLastLine (Lastline: integer);
procedure SetTime (Time: integer);
procedure SetGrid (Grid: TStringGrid);
end;
implementation
uses ProcThread, untMain;
var
ThreadCount_local: integer;
ListCount_local: integer;
LastLine_local: integer;
Time_local: integer;
Grid_local: TStringGrid;
procedure thRelayThread.Execute;
var
PrThread: thProcThread;
Counter, I: integer;
L: TList;
begin
counter := 1;
while Lastline_local < ListCount_local do
begin
if untMain.cancelFlag = true then break;
While not Terminated do
begin
L := TList.Create;
while (counter <= ThreadCount_local) and (Lastline_local <= ListCount_local {-1}) do
begin
if untMain.cancelFlag = true then break;
PrThread := thProcThread.Create(true);
PrThread.SetURL(Grid_local.Cells[0, LastLine_local]);
PrThread.SetTime(Time_local);
PrThread.FreeOnTerminate := true;
PrThread.Resume;
counter := counter +1;
Lastline_local := Lastline_local +1;
Synchronize(@UpdateStatus);
L.Add(PrThread);
end;
for I := L.Count -1 downto 0 do
begin
if TThread(L[I]).WaitFor = 0 then
begin
TThread(L[I]).Free;
L.Delete(I);
end;
end;
counter := 1;
L.Free;
end;
end;
end;
procedure thRelayThread.SetThreadCount (ThreadCount: integer);
begin
ThreadCount_local := threadCount;
end;
procedure thRelayThread.SetListCount (ListCount: integer);
begin
ListCount_local := ListCount;
end;
procedure thRelayThread.SetLastLine (LastLine: integer);
begin
LastLine_local := LastLine;
end;
procedure thRelayThread.UpdateStatus;
begin
untMain.lastLine := LastLine_local;
untMain.frmMain.StatusBar1.Panels.Items[1].Text := intToStr(lastLine_local);
end;
procedure thRelayThread.SetTime (Time: integer);
begin
Time_local := Time; //vreme za koje ce da ubije IE
end;
procedure thRelayThread.SetGrid (Grid: TStringGrid);
begin
Grid_local := Grid;
end;
end.
I na kraju Thread koji otvara IE:
unit ProcThread;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Process, IniFiles;
type
thProcThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
public
procedure SetURL (URLstring: string);
procedure SetID (ID: integer);
procedure SetTime (Time: integer);
end;
implementation
uses untMain;
var
URL_local: string;
Time_local: integer;
function origpath: string; // posle UPX-ovanja aplikacija nije vise svesna svog foldera u kome egzistira
var
tmp: string; // pa je potrebna ova funkcija
begin
tmp := StrPas(argv[0]);
tmp := ExpandFileName(tmp);
tmp := ExtractFileDir(tmp);
tmp := IncludeTrailingPathDelimiter(tmp);
result := tmp;
end;
procedure thProcThread.Execute;
var
AProcess: TProcess;
Ini: TIniFile;
explorer: string;
begin
Ini := TIniFile.Create(origpath + 'witchhunter.conf');
try
explorer := Ini.ReadString('Explorer', 'path', '"C:\Program Files\Internet Explorer\IEXPLORE.EXE"');
finally
Ini.Free;
end;
explorer := explorer + ' ';
AProcess := TProcess.Create(nil);
AProcess.CommandLine := explorer + URL_local;
AProcess.Execute;
if Time_local <> 0 then
begin
sleep(Time_local * 1000);
AProcess.Terminate(0);
end;
AProcess.Free;
end;
procedure thProcThread.SetURL(URLstring: string);
begin
URL_local := URLstring;
end;
procedure thProcThread.SetTime(Time: integer);
begin
Time_local := Time;
end;
end.
Ne znam gde gresim, ali desava se sledece:
Podesim da otvara po pet threadova paralelno, i otvore se pet IE-ova, ali svi otvaraju istu stranicu, i to onu petu iz grupe od pet.
|
|
|
|
Poslao: 09 Feb 2006 16:46
|
offline
- beli0135
- Executor
- Pridružio: 03 Jan 2005
- Poruke: 2990
- Gde živiš: Beograd
|
Zato sto koristis globalnu varijablu URL_local koja je ista za sve instance istog thread-a.. on ga postavi 5x ali vazi samo zadnji.
Premesti URL_Local u Private kao FUrl_Local i kreiraj isti property (to se zove "kako Bog zapoveda"), i nemoj nikada, ali ponavljam, NIKADA vise da koristis globalne varijable osim u slucaju da ne postoji drugo resenje.
|
|
|
|
Poslao: 10 Feb 2006 10:18
|
offline
- bobby
- Administrator
- Pridružio: 04 Sep 2003
- Poruke: 24135
- Gde živiš: Wien
|
@Beli
Eto, naucih nesto novo, nisam znao da su globalne varijable iste za sve instance, i nisam znao da osim procedura i funkcija mogu da trpam i varijable u private sekciju.
Hvala.
@Srki
Na FPC-u, Terminated nije exportovan (public), pa sam morao da ga upotrebim kao gore. Neznam sada da li je i na Delphiju isto.
|
|
|
|