Malo komplikovaniji problem sa threadovima

Malo komplikovaniji problem sa threadovima

offline
  • 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.
offline
  • Srđan Tot
  • Am I evil? I am man, yes I am.
  • Pridružio: 12 Jul 2005
  • Poruke: 2483
  • Gde živiš: Ljubljana

Bobby, Bobby... Smile

TThread klasa ima property Terminated koji je True kada thread zavrsi s poslom.

Napravis jednu listu (npr. L: TList) i uradis
while not Uslov do begin   // izbacujes gotove threadove iz liste   for I := L.Count - 1 downto 0 do     if TThread(L[I]).Terminated then     begin       TThread(L[I]).Free;       L.Delete(I);     end;   // dodajes nov ako ima mesta   if (L.Count < MAX_BROJ_THREADOVA) and ImaJosPodataka do   begin     T := TMojThread.Create(Parametar);     // Postavis sve za T sto treba, povecas brojac za stringlist i slicno    L.Add(T);   end;     Application.ProccessMessages; end;

Pisao sam iz glave, ali si shvatio koncept... nemoj da komplikujes program bez potrebe Smile



offline
  • 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.

offline
  • Emil Beli
  • 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.

offline
  • 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.

Ko je trenutno na forumu
 

Ukupno su 784 korisnika na forumu :: 6 registrovanih, 0 sakrivenih i 778 gosta   ::   [ Administrator ] [ Supermoderator ] [ Moderator ] :: Detaljnije

Najviše korisnika na forumu ikad bilo je 3466 - dana 01 Jun 2021 17:07

Korisnici koji su trenutno na forumu:
Korisnici trenutno na forumu: bojank, Koridor, Litostroton, LUDI, stegonosa, wizzardone