Van egy osztályom, amit egy objektum billentyűkkel szerkesztésére használok. A megjelenítést továbbadja egy másik osztálynak, ezért szálként szeretném futtatni:
import java.awt.KeyListener;
import java.awt.KeyEvent;
public class Editor implements Runnable, KeyListener{
public Editor(){
}
public void run(){
while(true){
//Itt adom tovább a megjelenítendő cuccot.
}
}
public void keyPressed(KeyEvent e){
}
public void keyReleased(KeyEvent e){
}
public void keyTyped(KeyEvent e){
//Itt történik a szerkesztés.
}
}
Van egy másik, amiben létrehozom és futtatom:
import javax.swing.JFrame;
class SwingFrame extends JFrame{
private Editor editke;
public SwingFrame(String title){
editke = new Editor();
initUI(title); // Jelölt sor 1
new Thread(editke).start(); //Jelölt sor 2
}
private void initUI(String title){
setTitle(title);
addKeyListener(editke);
//Meg egyebek
}
}
Ha így csinálom, akkor nem figyel a billentyűkre, ha a két jelölt sort felcserélem, akkor meg nem adja tovább a vuvvot. Ha jól sejtem, akkor az a problémája, hogy egy objektumot két szálon akarok futtatni (ugye az event thread meg a saját). Igazam van-e? Ha valóban ez, akkor kell egy külön figyelő, ami meg az editornak üzenget? Vagy valami mást próbáljak meg?
- 5777 megtekintés
Hozzászólások
Attol fugg, hogy mit csinalsz keyTyped()-ben.
----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
--> YouTube csatornám
- A hozzászóláshoz be kell jelentkezni
Semmi különöset. Van egy objektum, amit szerkeszt az editor,ez a szerkesztés történik ott. Konkrétan:
[code]
switch(e.getKeyCode()){
case KeyEvent.VK_UP:{
//feljebb lép, ha tud
break;
}
case KeyEvent.VK_DOWN:{
//lejjebb lép, ha tud
break;
}
case KeyEvent.VK_LEFT:{
//balra lép, ha tud
break;
}
case KeyEvent.VK_RIGHT:{
//jobbra lép, ha tud
break;
}
default:}
//beírja e.getKeyChar() -t
}
}--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Pont az a kerdeses, hogy mijen ojjektumot csesztetsz, ha egy grafikus vezerlo, akkor nem kene kulon szalrol kozvetlenul piszkalni, lasd Swingutilities metodus, ahogy fenntebb mar irtak.
De ettol meg kene reagaljon az esemenyekre, legfeljebb neha, ha nagyon szerencses lennel, eszrevenned,hogy hibasan jelenik meg. Szerintem a hiba mashol van elasva, de ennyi kodbol nem lehet megallapitani...
----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
--> YouTube csatornám
- A hozzászóláshoz be kell jelentkezni
Vagy az eseményekre reagál, vagy küldi az üzeneteket. Ahogy olvasgatom, az AWT szál egyszerűen blokkolja a Runnable szálat. Reagál az eseményekre, csak éppen nem történik semmi.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Az EDT nem blokkol semmit, a ket szal alapbol nem "akad ossze". Akkor lehetnek gondok, ha ket szal ugyanazt az ojjektumot piszkalna, ez viszont mar a te kododban lesz.
----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"
--> YouTube csatornám
- A hozzászóláshoz be kell jelentkezni
Az Editor egy KeyListener formájában figyeli az eseményeket, majd ezek függvényében egy táblázatot változtat. A Runnable implementációja pedig eme táblázatot küldi tovább a Display osztály egy példányának. És szerintem akkor ez lesz a hiba oka.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
duplaztam, bocs
- A hozzászóláshoz be kell jelentkezni
Miért akarod szálra tenni a key listenert? Konkrétan ezt a keretrendszert nem ismerem, de általában elég jól definiálni szokták, hogy az eseménykezelők hogyan működnek a UI szálon, mit kell tenned, hogy ne blokkoljon és ne is okozzon problémákat, stb.
- A hozzászóláshoz be kell jelentkezni
Nos, két szálat csináltam. Eredetileg egyetlen osztály felelt a szerkesztésért és a megjelenítésért, de kissé túlzásnak találtam egy idő után, ezért kettévettem. Az egyik szál felel a szerkesztésért, ehhez kapcsolódik a KeyListener (meg fog egy MouseListener is, ha a probléma megoldódott).
Leginkább önképző módon tanulom az egészet, egyrészt a könyvtárból veszegetek ki könyveket, másrészt meg próbálok rákeresni a neten a problémáimra, aztán vagy sikerül, vagy nem, harmadrészt meg az API dokumentációkat próbálom megérteni. Egyelőre itt tartok, ezzel most nem sikerült előrébb jutni.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
A run-ban mit csinálsz a végtelen ciklusban? [btw.: ilyenkor elképzelhető, hogy illik daemon threaddé tenni a thread-et, hogy ha már csak az fut, akkor kiléphessen a JVM)
Az, hogy az eseményszálon kívül módosítasz UI elemeket messze nem garantált, hogy eredményez bármi változást a UI-n.
BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)
- A hozzászóláshoz be kell jelentkezni
Az így általában nem fog működni, hogy gondolsz egyet, és kettészedsz vagy más szálra teszel valamit. Egy UI keretrendszerben tipikusan elég kötött, hogy melyik szálon mi fut.
Ismét általánosságban beszélek. A bill. leütésekre és egyéb inputokra a UI szál fog figyelni, ő a listener a szó szoros értelmében, akár tetszik, akár nem. Amit te beregisztrálsz eseménykezelőnek, ő már csak passzívan reagálni fog ezekre az eseményekre. Ez szükségszerűen szintén a UI szálon fog történni, mert a UI keretrendszer ott hívja meg, nincs más választásod. A másik dolog, hogy a UI szálon kívülről a UI-ra nyúlni általában veszélyes és tilos. Jól definiált interfészek szoktak lenni, hogy ezt biztonságosan meg lehessen tenni. Két szál közötti adatcserére megvannak a mindenféle üzenetküldési módszerek, de ha X függvény (pl. a bejövő esemény) meghív egy Y függvényt (pl. a te eseménykezelőd), az nem fog csak úgy magától szálat váltani.
- A hozzászóláshoz be kell jelentkezni
A két szél közötti adatcsere ok, azzal nincsen gond. Sőt, eleve ezzel kezdtem, mert pontosan ezt is terveztem. Nem is nyúlok szálon kívülről a UI-ra. pont hogy az adatcsere révén küldöm el, mi változott, és egy másik osztály felel a megjelenítésért. Csak éppen a változtatás miatt kell nekem a figyelő, amin keresztül a változtatás történik. De, ha jól értelmezem, akkor ennek nem is kell szálnak is lennie, anélkül is menne az adatcsere?
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Szerintem azt kevered össze, hogy csak azért, mert valami két objektum, még nem szükséges hozzájuk két külön szálnak is tartoznia.
- A hozzászóláshoz be kell jelentkezni
Valóban, de a kettő (szerintem legalábbis) két külön dolgot csinál, egymástól függetlenül.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Attól még miért kéne párhuzamosan futniuk? Szerintem nem érted a szálak jelentőségét illetve nem vágod, hogyan működik egy OOP program végrehajtása.
- A hozzászóláshoz be kell jelentkezni
Az meglehet. Na mindegy, megpróbálom akkor anélkül.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Nem bonyolult: objektumoknak és szálaknak semmi közük egymáshoz. Az objektumok kizárólag a memóriában élnek, csak adatot tárolnak, és a memória az egész processzben közös, minden szál ugyanazt látja (meg persze a programkód is ott van valahol a memóriában). A szál meg inkább a CPU-ban él, itt folyamatosan fut valami (akár alvás/várakozás is lehet), van neki egy stack-je a memóriában, és láncban hívogatják egymást a metódusok (nézz meg egy stack trace-t). Csinálhatsz több szálat, de csak akkor érdemes, ha olyan dolgokat akarsz végezni, amik amúgy sok időre blokkolnák egymást. Ezen felül csak azt kell megérteni, hogy egy UI keretrendszer kiköti neked, hogy ő a saját dolgait milyen szálon hajlandó végezni.
- A hozzászóláshoz be kell jelentkezni
Na, így már tisztul a kép. Akkor tehát ha van egy ablakkeret, az fogja képviselni egyben a UI szálat is. Ehhez hozzárendelhetem a szerkesztő osztályt, illetve a megjelenítő osztályt. Ha pedig ezek között kell kommunikálni, akkor az is mehet ebben a szálban? Vagy ehhez csináljak egy új szálat már?
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Új szál akkor kell, ha valami olyat kell csinálni, ami "sokáig" (> pár századmásodperc) tart. Pl.: egy tudományos számológép minden funkciója még annyira gyors, hogy ha a UI thread-ben végzed is a számítást, a felhasználó nem fogja észrevenni, hogy megfagyott az UI. Ha mondjuk viszont ugyanez a számológéped tud mátrix műveletet tetszőleges méretű mátrixokkal, akkor érdemes átvezetni egy másik szálra a számítást, mert két nagyobb mátrix összeszorzásáig már nem biztos, hogy a fehér ablakot akarná bámulni a user :)
Az ablakkeret (ez gondolom egy JFrame akar lenni) igazából maga nem képviseli a szálat, az AWT-hoz a Swing szépen regisztrálja az ablakod és a kettő igazából független (leszámítva a JFrame.EXIT_ON_CLOSE default action-t, mert akkor szépen lelövi a JVM-et, így az event thread-et is :) ).
BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)
- A hozzászóláshoz be kell jelentkezni
Igen, egy JFrame.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
"Akkor tehát ha van egy ablakkeret, az fogja képviselni egyben a UI szálat is."
Nem.
(na most a valóban szakmabeliek ne olvassanak tovább, megpróbálom leírni egy kezdőnek a dolgot. Nem lesz teljesen szabatos).
A szálak végrehajtási egységek, azt mondják meg, hogy egymástól függetlenül hány művelet hajtódik végre egyszerre. A JVM ütemezi a szálakat, és hajtja őket végre. Ha többmagos processzorod van, akkor egy időben több szál is fog futni.
A szálak által végrehajtott műveletek nem mások, mint az objektumok metódusainak meghívásai, a kódot hajtják végre.
Az objektumok maguk pedig ettől független létezők, az egyes szálak által végrehajtott kód mindig valamelyik objektum valamelyik metódusát hívja meg.
Van egy speciális szál, az ún. Event Dispatch Thread. Ez felel az operációs rendszer felől jövő (pl. megmozdul az egér, lenyomtak egy gombot) és felé menő (rajzolj ide, rajzolj oda stb.) üzenetek fogadásáért/küldéséért.
Amit te csinálsz, amikor UI-t programozol, az az, hogy a UI szálra (EDT) küldesz olyan üzeneteket, amelyek végül ablakrajzolásban fognak megtestesülni, vagy éppen onnan fogadsz üzeneteket.
Van ugyebár az az eset, amikor 1 objektum metódusát egyszerre több szál akarná meghívni. Ez nagyon problémás lehet, ezért kell azokat az objektumokat, amelyeket több szál is használhat, szálbiztosan megírni. Ennek azonban teljesítményproblémái vannak (lockok használata és társai), viszont a UI pont az a terület, ahol ez nem megengedhető.
Itt az az általános megállapodás, hogy a sebesség érdekében a UI objektumai nem szálbiztosak, de cserébe gyorsak.
Ez jelenti azt is, hogy a programozónak kell biztosítania azt, hogy megfelelően, csak az EDT-ből kezeli a UI objektumokat.
Valamint ezért van az is, hogy UI szálon nem végzünk lassú I/O műveleteket: amíg az IO művelet fut, addig a UI szál nem tudja a többi eseményt feldolgozni, emiatt "lefagy" a program.
A háttérműveleteket, azaz amik nem UI műveletek, hanem feldolgozások, I/O műveletek, számítások, más szálakon kell elvégezni és nem a UI szálon.
Ilyenkor persze adódhat, hogy a kiszámított értéket át kell adni a UI-nak valahogy: erre való az invokeLater metódus: az argumentumként kapott Runnable objektumot a UI szálon fogja futtatni.
Tipikus programozási minta:
kialakítom a UI-t (eseménykezelők stb) a UI threaden.
Történik egy esemény, azt a lekezelő objektum megkapja, ha éppen nem triviális a lekezelés feladata (pl. egy gomb megnyomása elindít egy komoly file feldolozó műveletet), akkor egy háttérszálon elindítom a műveletet. A UI reszponzív marad, a háttérszál dolgozik a háttérben azon a feladaton, aminek semmi köze a UI-hoz (épp ezért nem is kell, hogy a UI szálon fusson), majd amikor végzett, akkor az invokeLater segítségével szól a UI-nak, hogy készen vagyok, módosítsd a felületet.
- A hozzászóláshoz be kell jelentkezni
Aha. Tehát akkor az eseménykezelőt ne írjam meg külön osztálynak, hanem a megjelenítő osztályban hozzam létre, ha jól értem. (Nagy varia nincsen, egy táblázat egyetlen elemét lehet egyszerre módosítani - egészen pontosan egy keresztrejtvényét.) Mondjuk így bizonyos mértékig egyszerűbbé válik az életem, csak éppen kicsit sokminden lesz egyetlen objektumba zsúfolva, ami bántja a szépérzékemet. Viszont most néhány másik gondom is megoldódni látszik.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Az eseménykezelő legyen külön osztály (akár még inner class is lehet), de ne legyen külön szál.
- A hozzászóláshoz be kell jelentkezni
Akkor tehát jó volt az eredeti elgondolásom, csak túlbonyolítottam?
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
A szálakat felejtsd el. Kompletten.
- A hozzászóláshoz be kell jelentkezni
Igenis! :) Eredetileg mondjuk az volt a terv, hogy külön kezelem a szerkesztést és külön a megjelenítést, és valamiért ezeket párhuzamosítani akartam, innen jöttek a szálak. De akkor egyelőre hanyagolom.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
az fogja képviselni egyben a UI szálat is.
Nem. Semmi nem képvisel semmilyen szálat. A szál ugyanis a program futása maga. Azaz az egyes objektumokhoz tartozó kód végrehajtása.
Neked, kezdő programozóként tökéletes megoldást nyújt elsőre, ha pontosan egyetlen egy szállal dolgozol csak, és az hajtja végre az összes osztály össszes objektumának összes kódját. Ennek rengeteg előnye van, és egyetlen hátránya: amíg a programod el van foglalva valamivel (pl. egy fájl beolvásása), addig a felület nem reagál (Windowsban ez a "this program has stopped responding" c. történet). Ezzel a hátránnyal az esetek 98%-ában tökéletesen együtt lehet élni. Pl. a Microsoft Windows jelentős része tökéletesen megfelel így a felhasználóknak...
Aztán ha már majd jobban értesz a dolgokhoz, akkor lehet erőlködni további szálak indításával, ami jelentősen el tudja bonyolítani az életet.
- A hozzászóláshoz be kell jelentkezni
jelentősen el tudja bonyolítani az életet.
Veszem észre. :/ Na mindegy, akkor maradok egy (haj)szálon.
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Semmi nem képvisel semmilyen szálat.
Értem, hogy mit írsz, tudom, hogy jóval több is, mint maga az osztály példánya, de a Thread példány, ami reprezentálja a szálat, az "képviseli" a szálat, ugyanúgy, ahogy a Személy osztály egy példánya reprezentálja Kovács Józsefet.
BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)
- A hozzászóláshoz be kell jelentkezni
Hali,
szerintem a problémád összefüg az EDT-vel, így én a SwingUtilies.invokeAndWait és a .invokeLater környékén nézelődnék. :)
http://docs.oracle.com/javase/8/docs/api/javax/swing/SwingUtilities.htm…-
http://docs.oracle.com/javase/8/docs/api/javax/swing/SwingUtilities.htm…-
- A hozzászóláshoz be kell jelentkezni
Pontosan. A GUI egyszalu, GUIs kod csak azon a szalon futhat, ezt meg kene erteni.
- A hozzászóláshoz be kell jelentkezni
Ö... izé... vagy félreértek ebből valamit vagy...
OK, hülye vagyok hozzá, legyen ez az alap.
Azt hiszem, te már a második vagy két napon belül, aki szó szerint ezt írja.
Hogy értsem?
Mi van a játékokkal pl., ahol mondjuk mozog több alak, közben valaki matat a játékmezőben?
Ez nem többszálú GUI?
Vagy mit nem értek?
- A hozzászóláshoz be kell jelentkezni
A játék teljesen más téma, ott annyi szál van, amennyit épp az adott engine csinál, és elég sokféle engine van. Itt most ablakokra, gombokra meg beviteli mezőkre kell gondolni. Egy tipikus Android, .NET winforms, meg hasonló GUI-k esetében a keretrendszer 1 db UI szálat definiál, ahol megy az event loop. Csinálhatsz egyéb dolgokat, de azt már csak másik szálon, hogy ne blokkold a UI szálat. Egyszerűen nincs szükség több szálra, mert két esemény között rengeteg idő eltelik és semmi dolga nincs a programnak, egy játékkal ellentétben, ami kicsit bonyolultabb.
- A hozzászóláshoz be kell jelentkezni
Ebből a környezetből: progress bar (mire ez végre eszembe jutott... a játékot csak kínomban írtam, mert nem jutott eszembe a "progress bar" kifejezés)
Lehetőleg egy Cancel/Abort gombbal, aminek az eseményeit figyelni kell.
Ezt én többszálúként írnám, ha ilyesmire vetemednék.
(bár lehet másképp is, az tény)
- A hozzászóláshoz be kell jelentkezni
Alapvetően a legtöbb egyszerű UI-t meg lehet csinálni 1 szálon. Azon az 1 szálon megy az event loop. Az ablakkezelő/OS néha küld egy eseményt, hogy légyszi rajzold újra magad. Szintén az OS néha küld egy eseményt, hogy egérrel kattintottak vagy lenyomtak egy billentyűt. Az ablakkezelő/OS egy kívülálló valami, másik processz többnyire, tehát nem számít bele. Ez mind bőven elfér 1 szálon, és közben átlag 0% CPU-t fog enni. Ezt a szálat pedig nem is te írod, hanem a UI keretrendszer, mert nem szoktunk 0-ból event loopot írni manapság. Ha nem csak eseményekre reagálsz, hanem aktívan dolgozni is akarsz valamit a háttérben, akkor a további szál pedig kötelező, mert pl. lefagy a felület és nem rajzolja újra magát. Ekkor annyi szál van, ahányon csak dolgozni akarsz, ez a UI szálat nem érdekli.
- A hozzászóláshoz be kell jelentkezni
Az oké, hogy több szálú, de az nagyjából így néz ki:
LongRunningTask extends Thread
-> run() [ez a metódus fut a másik a szálban indítás után, időnként [pl. nyomtatásnál oldalanként, adatbázisfeldolgozásnál rekordonként stb.] figyeli, hogy be van-e állítva az "ájjálle" flag és meghívja a frissít() metódust]
-> pause()/abort()/stop()/akármi() [ezt a metódust hívhatod az eseményszálból, beállítja a run által figyelt "ájjálle" flag-et]
-> frissít() [ez a LongRunningTask objektum által reprezentált szálon fut és csak annyit csinál, hogy (SwingUtilities.invokeLater/invokeAndWait-el) beadobál a UI thread eseménysorába új futtatandó kódokat, amik frissítik a megfelelő progress bart - amikor éppen "ráér" a UI thread]
AblakUI : JFrame
-> Start gomb: akcióra: (foo = new LongRunningTask()).start()
-> Stop gomb: akcióra: foo.stop();
Aztán még lehet játszani vele, hogy a LongRunningTask-nak pl. adsz egy sikerült/nem sikerült "callback-et" (eseményt) és a Start gomb lenyomásakor futó ActionListener beállít egy-egy eseménykezelőt, ami áttér a UI thread-be és értesíti a usert.
Több szálú, de a UI szálat csak egy helyről bántod, a szálak közti értesítés-átadást meg így-úgy megoldod (többnyire valami queue-val, amit a Swing-nél az AWT eseményszál már megold, a SwingUtilities-zel meg kényelmesen módosítani is lehet) - a másik irány viszont már kérdésesebb.
BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)
- A hozzászóláshoz be kell jelentkezni
OK, végül is fejesjoco ugyanezt írta, csak más szavakkal.
- A hozzászóláshoz be kell jelentkezni
Jaja, csak a fránya párhuzamosítás és a szinkronizálás hiánya miatt az elvégzett feladatokkal egymásra futottunk :) [amikor elkezdtem bepötyögni, fejesjoco hozzászólása még nem volt ott]
BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)
- A hozzászóláshoz be kell jelentkezni
Esetleg a következő járható út?
Van egy JFrame utód osztály, amihez tartozik a megjelenítő és a szerkesztő. A szerkesztő egyszerűen egy KeyListener utód, a megjelenítő meg JComponent. A szerkesztő értelemszerűen a kerethez addKeyListenerrel van kötve, a megjelenítő meg egyben egy Runnable is, és SwingUtilities.invokeAndWait() metódussal van rendszeresen meghívva, hogy frissítsen, ha történt változás. A szerkesztő és a megjelenítő pedig direktben egymással kommunikál.
Esetleg még annyi kérdésem van, hogy ha egy osztály két Listener implementációja egyszerre, akkor az fog-e zavart kelteni az erőben?
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni
Mi az oka annak hogy invokeAndWait-et hívsz, és nem invokeLater-t?
- A hozzászóláshoz be kell jelentkezni
Azonkívül, hogy eltévesztettem? Semmi. :)
--
Fight / For The Freedom / Fighting With Steel
- A hozzászóláshoz be kell jelentkezni