- Učlanjen(a)
- 30.07.2000
- Poruke
- 5,577
- Poena
- 770
Teme sam se posredno dotakao u threadu o CppBuilderu za linux, pa bolje da se tema prenese na novi thread, nego da se nastavi na "civilno" pitanje 
Elem, iako je ovo namenjeno pocetnicima u kylixu, ne zelim da objasnjavam sta je multiprocessing ili multithreading (ako vam je vec zatrebalo, onda znate sta je to), nego samo da skrenem paznju na par manjih zamki.
Najvecu zamku predstavlja X server. Dakle, mnoge od ovih stvari se ne bi desile ukoliko se radi o nekoj konzolnoj aplikaciji (recimo daemonu), ali za desktop ...
1. multithreading
Vrlo je diskutabilno koje komponente/klase su thread-safe, a koje ne (TTimer, na primer, to definitivno nije). TThread klasa ima isti interfejs kao i pandan u Delphiju, ali metod Synchronize() jednostavno ne zavrsava uvek posao. Kada se poveca broj komponenti, kao da se refresh ne odvija dovoljno brzo/sigurno i aplikacija puca uz poruku X servera da "nije mogao da sinhronizuje ...". Bilo koji linux programer ce zajedljivo primetiti da vam to nije windows, te da ne koristite multithreading, nego multiprocessing. Medjutim, resenje moze biti tu negde izmedju. Uzmimo kao primer taj kilavi TTimer, te da treba voditi racuna o nekoliko razlicitih eventova (na 100ms, na 500ms i na 1000ms) u relativno zahtevnijem programu. U tom slucaju, mozda je najbolje TTimer skroz izdvojiti iz glavnog programa, u sasvim poseban izvrsni fajl, koji nema "u sebi" multithreading. Takav programcic se napravi kao konzolni, bez ikakvog GUI-ja i jedini zadatak mu je da sadrzi timer, te da na definisani interval (recimo 100ms) "tikne", tj saopsti glavnom progamu da je otkucao jedan tick. Kako ce ta dva procesa komunicirati, zavisi od vaseg umea u IPC-u; bilo preko sherovanog dela memorije, bilo preko signala, bilo preko socketa, ... vas izbor. Recimo da ste se opredelili za socket komunikaciju, posto vam semafori nisu jaca strana. U tom slucaju, mali "ticker" treba da sadrzi samo TTimer i neki UDP klijent objekat, tako da na definisani interval posalje telegram (record definisan prema vasim potrebama) na odredjeni port. I to je sve sta se njega tice.
Glavni program treba da sadrzi klasu koja potice od TThreada, te sadrzi UDPserver klasu (recimo Indy paket), koja ima mogucnost da "osluskuje" na doticnom portu. Ukoliko je telegram malo komplexniji, nije lose da se polja mapiraju na propertyje tog threada. Nadalje, treba vam jedna globalna (da mogu svi threadovi da joj pristupaju) varijabla tipa TSimpleEvent
koja ce obelezavati kada je telegram stigao (nazovimo je GUIevent). Treba definisati OnUDPread event-handler tog UDPservera, gde se primi stream preko socketa, izdvoji telegram i raspodeli polja, te okine taj GUIevent (sa GUIevent.SetEvent). E sad, da ne bi bilo sve tako lepo i jednostavno, treba vam jos jedan thread, koji ce sve to da sinhronizuje. Dakle, spoljni proces hvata tajmer, salje telegram glavnom programu. Glavni program ima thread u kojem cuci UDPserver i hvata te telegrame, te okida event "telegramStigao". Zasto praviti sad jos jedan dodatni, "singhronizacioni" thread ? Zbog nekoliko razloga; jedan ozbiljniji program koji bi trebao da radi "glatko", treba da reaguje odmah. Prilikom primanja streama sa socketa, moguci su razni oblici obrade tog telegrama. Ostatak programa mozda ne moze da ceka da se to zavrsi, nego te stvari mogu paralelno da se izvrsavaju; dakle, primi telegram i signaliziraj "tick". Dok se vrsi analiza telegrama (to se vec nalazi u posebnom threadu), neka se paralelno izvrsava sinhronizacija aktivnosti s obzirom na pristigli tajmer. Da bi to stvarno bilo paralelno, treba nam jos jedan thread (paznja, to su sve male i jednostavne klase, treba ih samo pazljivo uvezati u "mrezu"). Drugi veliki razlog koriscenja dodatnog sync threda je u cpu overheadu. Dakle, moze i bez njega, ali cete verovatno imati zauzece procesora 80-90% iako program stoji idle. Dakle, sinhronizaciju povlaci samo kada je potrebno - a upravo to radi ovaj sync thread. On samo ceka da GUIevent bude okinut i nista vise; npr:
[code:1]
procedure TsyncThread.Execute;
begin
while not terminated do
if GUIevent.WaitFor($ffffffff) = wrSignaled then
begin
Synchronize(Main.OnSimulTimer);
GUIevent.ResetEvent;
end;
end;
[/code:1]
OnSimulTimer je procedura koja radi ono sto bi inace radio OnTimer event-handler, da ste kojim slucajem ostavili TTimer u glavnom programu, npr, proverava config fajl, updatuje label koji sadrzi aktuelno vreme, itd. Posto se salju uvek kratki telegrami iste strukture u istim vremenskim intervalima, budite sigurni da je deltaT uvek isto. Ukoliko treba da se odreaguje na razlicite tajminge, onda moze da se postavi neki flag tipa SyncCounter, pa se kod citanja telegrama flag inkrementuje, pa tek ona okine GUIevent. A ovde u sinhro-threadu proverava da li je svaki peti event ili sta vam vec treba (if (SyncCounter mod 5)=0 then Synchronize(Main.SvakiPeti); )
Elem, iako je ovo namenjeno pocetnicima u kylixu, ne zelim da objasnjavam sta je multiprocessing ili multithreading (ako vam je vec zatrebalo, onda znate sta je to), nego samo da skrenem paznju na par manjih zamki.
Najvecu zamku predstavlja X server. Dakle, mnoge od ovih stvari se ne bi desile ukoliko se radi o nekoj konzolnoj aplikaciji (recimo daemonu), ali za desktop ...
1. multithreading
Vrlo je diskutabilno koje komponente/klase su thread-safe, a koje ne (TTimer, na primer, to definitivno nije). TThread klasa ima isti interfejs kao i pandan u Delphiju, ali metod Synchronize() jednostavno ne zavrsava uvek posao. Kada se poveca broj komponenti, kao da se refresh ne odvija dovoljno brzo/sigurno i aplikacija puca uz poruku X servera da "nije mogao da sinhronizuje ...". Bilo koji linux programer ce zajedljivo primetiti da vam to nije windows, te da ne koristite multithreading, nego multiprocessing. Medjutim, resenje moze biti tu negde izmedju. Uzmimo kao primer taj kilavi TTimer, te da treba voditi racuna o nekoliko razlicitih eventova (na 100ms, na 500ms i na 1000ms) u relativno zahtevnijem programu. U tom slucaju, mozda je najbolje TTimer skroz izdvojiti iz glavnog programa, u sasvim poseban izvrsni fajl, koji nema "u sebi" multithreading. Takav programcic se napravi kao konzolni, bez ikakvog GUI-ja i jedini zadatak mu je da sadrzi timer, te da na definisani interval (recimo 100ms) "tikne", tj saopsti glavnom progamu da je otkucao jedan tick. Kako ce ta dva procesa komunicirati, zavisi od vaseg umea u IPC-u; bilo preko sherovanog dela memorije, bilo preko signala, bilo preko socketa, ... vas izbor. Recimo da ste se opredelili za socket komunikaciju, posto vam semafori nisu jaca strana. U tom slucaju, mali "ticker" treba da sadrzi samo TTimer i neki UDP klijent objekat, tako da na definisani interval posalje telegram (record definisan prema vasim potrebama) na odredjeni port. I to je sve sta se njega tice.
Glavni program treba da sadrzi klasu koja potice od TThreada, te sadrzi UDPserver klasu (recimo Indy paket), koja ima mogucnost da "osluskuje" na doticnom portu. Ukoliko je telegram malo komplexniji, nije lose da se polja mapiraju na propertyje tog threada. Nadalje, treba vam jedna globalna (da mogu svi threadovi da joj pristupaju) varijabla tipa TSimpleEvent
koja ce obelezavati kada je telegram stigao (nazovimo je GUIevent). Treba definisati OnUDPread event-handler tog UDPservera, gde se primi stream preko socketa, izdvoji telegram i raspodeli polja, te okine taj GUIevent (sa GUIevent.SetEvent). E sad, da ne bi bilo sve tako lepo i jednostavno, treba vam jos jedan thread, koji ce sve to da sinhronizuje. Dakle, spoljni proces hvata tajmer, salje telegram glavnom programu. Glavni program ima thread u kojem cuci UDPserver i hvata te telegrame, te okida event "telegramStigao". Zasto praviti sad jos jedan dodatni, "singhronizacioni" thread ? Zbog nekoliko razloga; jedan ozbiljniji program koji bi trebao da radi "glatko", treba da reaguje odmah. Prilikom primanja streama sa socketa, moguci su razni oblici obrade tog telegrama. Ostatak programa mozda ne moze da ceka da se to zavrsi, nego te stvari mogu paralelno da se izvrsavaju; dakle, primi telegram i signaliziraj "tick". Dok se vrsi analiza telegrama (to se vec nalazi u posebnom threadu), neka se paralelno izvrsava sinhronizacija aktivnosti s obzirom na pristigli tajmer. Da bi to stvarno bilo paralelno, treba nam jos jedan thread (paznja, to su sve male i jednostavne klase, treba ih samo pazljivo uvezati u "mrezu"). Drugi veliki razlog koriscenja dodatnog sync threda je u cpu overheadu. Dakle, moze i bez njega, ali cete verovatno imati zauzece procesora 80-90% iako program stoji idle. Dakle, sinhronizaciju povlaci samo kada je potrebno - a upravo to radi ovaj sync thread. On samo ceka da GUIevent bude okinut i nista vise; npr:
[code:1]
procedure TsyncThread.Execute;
begin
while not terminated do
if GUIevent.WaitFor($ffffffff) = wrSignaled then
begin
Synchronize(Main.OnSimulTimer);
GUIevent.ResetEvent;
end;
end;
[/code:1]
OnSimulTimer je procedura koja radi ono sto bi inace radio OnTimer event-handler, da ste kojim slucajem ostavili TTimer u glavnom programu, npr, proverava config fajl, updatuje label koji sadrzi aktuelno vreme, itd. Posto se salju uvek kratki telegrami iste strukture u istim vremenskim intervalima, budite sigurni da je deltaT uvek isto. Ukoliko treba da se odreaguje na razlicite tajminge, onda moze da se postavi neki flag tipa SyncCounter, pa se kod citanja telegrama flag inkrementuje, pa tek ona okine GUIevent. A ovde u sinhro-threadu proverava da li je svaki peti event ili sta vam vec treba (if (SyncCounter mod 5)=0 then Synchronize(Main.SvakiPeti); )