Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Muu suoritus pysähtymään yhden säikeen ajaksi (Java)

Sivun loppuun

hunajavohveli [18.01.2008 14:10:42]

#

Kun en ole juuri säikeitä ja tapahtumaohjelmointia harrastanut, niin kysytäänpä aluksi, vaikuttaako tällainen ratkaisu järkevältä reaaliaikaisessa pelissä: Minulla on pelimoottoriolio, jossa kaikki varsinainen toiminnallisuus pyörii yhdessä pääluupissa, joka siis hoitaa pelimaailman hahmojen toiminnan, näiden piirtämisen yms. Tämän lisäksi minulla on ikkunaolio, johon pelimaailma piirretään, ja joka kuuntelee ikkunaan kohdistuvia tapahtumia. Kun tapahtuma havaitaan, ikkunan ActionListener tallentaa sen erilliseen puskuriolioon. Myös pelimoottorilla on viite samaiseen puskuriin, josta pääluuppi voi sitten lukea sattuneet tapahtumat ja käsitellä ne täsmälleen siinä vaiheessa luuppia kuin on tarpeen.

Minusta tällainen toiminnallisuuden ja tapahtuminen käsittelyn erottaminen vaikuttaisi järkevimmältä. Kysyn vain lähinnä sen takia, että olen nähnyt tuhat ja yksi esimerkkiä niin yliopiston kurssimateriaalissa kuin muuallakin, joissa esim. peliolio on suoraan ikkunaluokan ja/tai tapahtumankuuntelijan aliluokka, kaikki mahdollinen hoidetaan yhdessä ja samassa luokassa, ja sellaistakin että pelihahmoja ohjataan kutakin omalla säikeellään. Jos olen käsittänyt oikein, niin tuollaisessa toteutuksessa pelihahmojen toiminta suhteessa toisiinsa olisi täysin riippuvainen Java-tulkista eikä ohjelmoijalla olisi mitään kontrollia toiminnan synkronointiin? Eli mahtaako tuollainen kuvaamani yksisäikeisen ohjelman tapainen pääluuppi olla kätevin ratkaisu?

Sitten varsinainen kysymys, eli onko mahdollista varata jollekin säikeelle koko suoritus, niin että kaikkien muiden säikeiden toiminta pysähtyisi täydellisesti, kunnes yksi tietty säie on saanut toimintansa loppuun? Edellä kuvaamassani systeemissä nimittäin sen jälkeen, kun tapahtuma on havaittu, joudutaan suorittamaan vielä suhteellisen monta operaatiota, jotta se saadaan lisätyksi puskuriin. Tällä välin ykkössäikeessä pyörivä pääluuppi ehtii tehdä monta kierrosta, ja peli siten edetä pitkälle ennen kuin esim. näppäimen painallukseen voidaan reagoida puskurista saadun datan perusteella. Pääluuppi tosin on vasta pelkkä tyhjä testisilmukka, joten tuo kierrosmäärä tullee vähenemään mahdollisesti lähes olemattomiin, kun luuppiin lisätään tavaraa. Siitä huolimatta tuntuisi jotenkin varmemmalta, että pääluupin suoritus pysähtyisi täysin tapahtuman havaitsemishetkestä siihen, että tapahtuma saadaan lisättyä puskuriin. Onnistuuko tämmöinen jotenkin?

Toivottavasti en puhunut ihan puuta heinää. Voin yrittää selventää sekavaa selitystäni pyydettäessä. :)

Mobel [18.01.2008 16:26:47]

#

Tämähän onnistuu niin, että laitetaan säikeet odottamaan wait:lla ja kun homma on suoritettu herätetään ne notifyAll:lla tai yksittäinen säie notify:llä.

Selvennän hieman:

public synchronized teeTemppu() throws InterruptedException
 while(tiedotSaatu==true){ //odotetaan muiden säikeiden tuomia tietoja
   System.out.println("Odotetaan muita säikeitä...");
   wait(); //säie odottaa notify-metodia
 }

 /*Do your kinky stuff here*/

 notifyAll(); //vapauttaa odottavat säikeet

}

Kirjoitin tuon suoraan tähän kenttään, joten en testannut. Pitäisi kuitenkin toimia näin. Eli wait() laittaa kyseisen säikeen odottamaan toisen säikeen valmistumista, mikä ilmoitetaan notifyAll()-metodilla.

hunajavohveli [18.01.2008 17:12:32]

#

Nyt en aivan ymmärtänyt. Ensinnäkin, tarkoititko "while(tiedotSaatu==false){"? Pysäyttääkö wait() säikeen, joka suorittaa wait():n, vai kaikki muut säikeet, vai minkä? Mitä metodia teeTemppu() vastaa?

Sain ongelman ratkaistua tilapäisesti niin, että tapahtuman sattuessa ikkunan tietorakenteeseen tallennetaan lippu, että tapahtumaa ollaan lisäämässä puskuriin, ja lippu poistetaan, kun tapahtuma on lisätty. Sitten laitoin yksinkertaisesti pääluuppiin ennen tapahtumankäsittelijää silmukan, joka pyörii niin kauan kuin ikkuna ilmoittaa, että se lisäilee tapahtumaa. Juuri kuten esimerkissäsi, mutta käyttämättä lainkaan wait():ia. Onkohan tällainen hyvä ratkaisu?

Mobel [18.01.2008 21:19:23]

#

Tietystihän tuon toisen rivin piti mennä juuri kuten korjasit eli "while(tiedotSaatu==false){".
Uskoisin, että tuo on ihan toimiva ratkaisu. Luulisi tuon wait():n ja notifyAll():n toimivan käytännössä samalla periaatteella. Tuskin tuossa toteutuksessa mitään ongelmaa ilmenee.

Wait() siis pysäyttää säikeen, joka sitä kutsuu. Tuo esimerkkini on tarkoitettu usealle samanlaiselle säikeelle, jotka ovat riipuvaisia toisistaan eli joutuvat odottamaan toisiaan.

Pääloopissa säikeen siis pitää kutsua wait():a ja tarkistaa onko se vapautettu notifyAll():lla, jolloin tapahtumat on jo lisätty ikkunaan.

hunajavohveli [20.01.2008 10:27:52]

#

Mobel kirjoitti:

Wait() siis pysäyttää säikeen, joka sitä kutsuu. Tuo esimerkkini on tarkoitettu usealle samanlaiselle säikeelle, jotka ovat riipuvaisia toisistaan eli joutuvat odottamaan toisiaan.

Aa, nyt käsitän. Jäin ihmettelemään, miten suoritus ikinä etenisi säikeen vapautukseen, jos se pysäytetään sitä ennen. :) Itselläni nuo eri säikeet siis eivät suorita samaa koodia, joten pitäytynen sitten omassa ratkaisussani, jos se kerran on hyvä.

kayttaja-2499 [20.01.2008 13:39:39]

#

wait-methodia kannattaa käyttää silmukassa jottei prosessointitehoa turhaan valu hukkaan.
Ohessa koodia:

private boolean flag;

public synchronized void setFlag(boolean flag) {
  this.flag = flag;
  notifyAll();
}

public synchronized void waitForFlag() throws InterruptedException {
  while(!flag) {
    wait();
  }
}

hunajavohveli [20.01.2008 16:07:32]

#

Kysyn nyt vielä selvyyden vuoksi, että ajaako saman asian while-silmukan sijaan

public synchronized void waitForFlag() throws InterruptedException {
  if(!flag) wait();
}

kayttaja-2499 [20.01.2008 16:35:44]

#

Koodisi ei täysin aja samaa asiaa. Kun jokin säie kutsuu kyseisen olion notifyAll-metodia, saman olion wait-metodia kutsuneet säikeet pääsevät takaisin ajoon. Eli koodisi periaatteessa ajaa saman asian, jos vain tiedetään ettei kyseisen olion notify- tai notifyAll-metodeja ajeta muissa yhteyksissä. while-silmukka vain tarkistaa että flag:n arvo on se, jolla säikeiden halutaan lopettaa odottaminen.

hunajavohveli [20.01.2008 22:28:14]

#

kayttaja-2499 kirjoitti:

Eli koodisi periaatteessa ajaa saman asian, jos vain tiedetään ettei kyseisen olion notify- tai notifyAll-metodeja ajeta muissa yhteyksissä.

Tässä tapauksessa tuo tosiaan tiedetään. Toteutanpa tämän siis tuollaisilla metodeilla.

kayttaja-2499 [21.01.2008 19:26:47]

#

Ei vara venettä kaada.

hunajavohveli [21.01.2008 20:29:02]

#

Tässä tapauksessa vain sattuu olemaan niin, että jos noita metodeja ajetaan muissa yhteyksissä, niin vikaa on jossain niin paljon, että tuo säikeiden odottelu on ongelmista pienin. Ylimääräinen tarkistus vaikuttaisi minusta vain purkkaratkaisulta, joka ei oikeasti ratkaise mitään, vaan ainoastaan peittelee todellista virhettä. Vielä kun on ohjelmoinnin harjoitustyö kyseessä, niin pyrin niin loogiseen rakenteeseen kuin mahdollista, jotta arvostelijallekin kelpaa.

kayttaja-2499 [21.01.2008 21:09:40]

#

"Always invoke wait inside a loop that tests for the condition being waited for. Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true."

Vapaasti suomennettuna:
"Kutsu wait-metodia aina silmukan, joka tutkii että halutut ehdot ovat täyttyneet, sisällä. Älä oleta että keskeytys liittyy haluttuun tilaan, tai että ehdot edelleen täyttyvät."

http://java.sun.com/docs/books/tutorial/essential/concurrency/guardmeth.html

hunajavohveli [21.01.2008 21:46:49]

#

En löydä tuolta mitään selitystä, miksei tuollaista saisi olettaa, jos virhettä ei kaiken järjen mukaan voi tapahtua. Minulla on tuolla notify() ainoastaan yhdessä metodissa, jonka tarkoitus on yksinomaan vapauttaa tuon yhden säikeen suoritus. Jos suoritus vapautuu eikä se johdu tuosta kyseisestä notify():sta, niin mistä se sitten voi johtua?

kayttaja-2499 [21.01.2008 22:07:45]

#

Myönnän että en ole mistään lukenut että järjestelmä jossain tilanteessa kutsuisi ennalta määräämättömän olion notify-metodia, enkä myös toisin päin.

Ajatus tässä on vain se että pelataan varman päälle ja tämän käytännön opittua myöhemmin ei välttämättä heti tule ongelmia.

Huomaa myös että if-lauseketoteutus ei toimi samalla tavalla tilanteessa, jossa setFlag-metodia kutsutaan false-arvon kanssa.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta