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 976 korisnika na forumu :: 50 registrovanih, 8 sakrivenih i 918 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: A.R.Chafee.Jr., Andrija357, anta, babaroga, Bokiboks, ccoogg123, darios, dejoglina, Dimitrise93, Djokislav, Djokkinen, doktor123, Dr.Strangelove, DragoslavS, drimer, Fog of War, Fulcrum, Georgius, goxin, HogarStrashni, ikan, ILGromovnik, Ilija Cvorovic, Još malo pa deda, kalens021, Karla, Koridor, kubura91, ladro, Levi, Luka Blažević, Mercury, MiroslavD, naki011, ozzy, pein, royst33, S1Mk3, saputnik plavetnila, Sir Budimir, sombrero, Srle993, Stefan M, suton, t84dar, Tas011, theNedjeljko, tmanda323, Webb, zeo