581322 Rinnakkaisohjelmistot (2 ov)

Erilliskuulustelu 5.10.2004

  1. Rinnakkainen suoritus (15 p)

    a) Tarkastellaan seuraavaa ohjelmaa

    int x= 0, y =4;

    co while (x!=y) x=x+1;

    // while (x!=y) y=y-1;

    oc

    Päättyykö ohjelman suoritus aina /joskus/ ei koskaan? Perustele vastauksesi. (5 p)

    Vastaus:
    Suoritus joskus päättyy ja joskus ei pääty. Päättyminen riippuu prosessien ajoituksesta.

    Arvostelusta:

    b) Mitkä ovat lukkiutumistilanteen (deadlock) syntymiselle välttämättömät ja riittävät ehdot ja miten kukin näistä ehdoista täyttyy aterioivien filosofien ongelmassa? Miten filosofien tapauksessa lukkiutuminen voitaisiin estää muuttamalla haarukoiden käyttösääntöjä? Tarkastele kutakin sääntöä erikseen. (10 p

    Vastaus:
    Välttämättömät ja riittävät ehdot:

    Filosofien tapauksessa: Arvostelusta:

  2. Tankkausta semaforipohjalta (15 p) Pienellä bensa-asemalla on vain yksi bensapumppu ja sitä käyttämässä asemanhoitaja, joka täyttää tankin ja ottaa maksun. Kesäaikaan asemalla silloin tällöin on asiakkaita ihan ruuhkaksi asti. Bensapumpun viereen mahtuu vain yksi auto kerrallaan. Muiden tankkausta haluavien on odotettava kauempana, kunnes tankattavana ollut auto on poistunut. Auto voi poistua vasta, kun se on tankattu ja maksu suoritettu. Laadi toimintakoodi tankkaamaan tuleville autoille (prosesseille auto[i = 1 to N]) ja asemanhoitajalle (prosessille hoitaja), kun tankkaavien autojen ja asemanhoitajan toiminnan synkronointiin käytetään semaforia.

    Eräs ratkaisu:
    Auton odotettava:

    Asemanhoitajan on odotettava:

    => kutakin odottamista varten tarvitaan oma semafori.
    Aluksi sallittua on vain yhden auton ajaminen pumpulle, joten vain sillä semaforilla on alkuarvo 1, muiden alkuarvo on 0.

    sem pumppu_vapaa =1,  # mutex-tyyppinen; valvoo, että vain yksi auto on kerrallaan tankkaamassa
        auto_pumpulla = 0,  # tankkaustoiminnan synkronointia varten: tankkaamisen voi aloittaa 
        tankattu_on = 0,   # tankkaustoiminnan synkronointia varten: tankkaus on valmis 
        maksu = 0;        # asemanhoitaja odottaa maksua (jonka jälkeen voi häipyä muihin
                           # hommiin siihen asti kunnes seuraava auto tulee tankattavaksi)
    
    process auto[i=1 to N]  {     # autoja N kappaletta
       while true {
          ajele ympäriinsä, kunnes tarvitsee tankata
          P(pumppu_vapaa);  # tässä jäädään odottamaan, jos pumppu on varattu 
          aja bensapumpulle tankkaamaan;
          V(auto_pumpulla);  # herätetään  asemanhoitaja toimimaan
          P(tankattu_on); # odotellaan, että tankkaus valmistuu
          maksetaan; 
          V(maksu);  # maksettu on 
          aja pois tankkausalueelta;   
          V(pumppu_vapaa);  # päästä seuraava auto tankkaamaan, joko jo 
                            #odottava tai joskus tuleva 
        }
    
           
    process asemanhoitaja {
        while true {
          P(auto_pumpulla); # odottele tankkaamaan tulevaa autoa
          tankkaa auto;
          V(tankattu_on);  # ilmoita tankkauksen valmistumisesta
          P(maksu);  #  jää odottamaan maksua
        }
        
    
    Arvostelusta:
    Koska pyydettiin käyttämään semaforia, niin muut ratkaisut (monitori, sanomanvälitys, yms) toivat 0 pistettä!

    Tehtävä oli kuitenkin melko hyvin osattu. Siitä sai täydet pisteet peräti kahdeksan opiskelijaa.

  3. Tuottajien ja kuluttajien synkronointi monitoria käyttäen (15 p) Tuottaja- ja kuluttajaprosessien välissä on rajallisen kokoinen puskuri (N alkiota), johon tuottajat tuottavat tietoa alkio kerrallaan ja josta kuluttajat sitä vastaavasti noutavat.
    1. Missä tilanteissa tässä tarvitaan prosessien poissulkemista ja synkronointia? Esitä sanallisesti, miten monitoriratkaisusi selviää näistä tilanteista. (3 p)

    Vastaus:

    Arvostelusta:

    b) Laadi tuottaja- ja kuluttajaprosessien toimintaa koordinoiva monitori. (8 p)

    	
    monitor puskuri {
       typeT pusk[N]; # N on puskurin koko 
       int  alku =0;  # puskurin alkuun
       int  loppu = 0; # puskurin loppuun eli ensimmäiseen tyhjään slotiin
       int  lkm =0;  # alkioiden (= tuotosten)  lukumäärä  puskurissa
           cond  mahtuu;  # lkm < N
           cond voi_ottaa;  # lkm > 0
          
         procedure vie (int  data) {
            while (count = = N) wait(mahtuu);
            pusk[loppu] = data; 
            loppu = (loppu + 1) % N;
            lkm ++;
            signal(voi_ottaa);
        }
     
     int procedure hae ( ) {
           int apu;
            while (count = =0) wait(voi_ottaa);
            apu = pusk[alku]; 
            alku= (alku  + 1) % N;
            lkm -- 
            signal(mahtuu);
            return apu;
        }  
      
    Ratkaisu löytyy myös kurssikirjasta sivulta 214.

    Arvostelusta:

    c) Laadi tuottaja- ja kuluttajaprosessien koodit, joista selkeästi käy ilmi, kuinka prosessit vievät tietoa puskuriin ja kuinka ne noutavat tietoa puskurista.(4 p)

    Eräs ratkaisu:

    process tuottaja[i=1 to N] {
      int data;
      while true {
           data = tuotettu_uusi_tieto( );
           call  pusk.vie(tieto);
         }
    }
    
    
    process kuluttaja[i=1 to M] {
      int data;
      while true {
      data =  call  pusk.hae(tieto);
      kuluta(data);
        }
    }  
    
    
    Arvostelusta:

  4. Kommunikointi sanomanvälitystä käyttäen. (15 p) Sääennustus on toteutettu rinnakkaisena laskentana. Ennustettavan alueen ilmatila on jaettu kuutioihin ja kunkin kuution ennusteesta huolehtii yksi solmukone. Laskenta etenee vaiheittain:
    • Alkuarvoista lähtien kukin solmu laskee kuutiolleen ensimmäisen ennusteen (esimerkiksi sään tunnin kuluttua).
    • Kun ennuste on valmistunut, niin solmu ilmoittaa ennustetun tilan naapureilleen.
    • Saatuaan kaikilta naapureilta näiden ennusteet solmu aloittaa seuraavan vaiheen ja laskee seuraavan ennusteen (esim. säätilan kuutiossa seuraavan tunnin kuluttua).
    Näin jatketaan kunnes ennusteet on saatu halutulle aikavälille (esim. kolmen vuorokauden ennuste). Laskentaohjelmat kommunikoivat käyttäen sanomanvälitystä ja globaaleja kanavia.

    Kirjoita yhden solmun laskenta-algoritmin normaalivaiheen toimintaa ohjaavan ohjelman oleelliset osat (sovelluksen käynnistys- ja lopetusvaiheesta ei tarvitse välittää). Esittele kanavat ja täsmennä send/receive -rutiinien synkronointisemantiikka (blocking vai non-blocking).

    Ratkaisu:

    globaalit määrittelyt
    
    
    typedef    ennuste   .... ;         #
    sääennustetietue
    chan mailbox[N,N] (sääennuste
    arvo)
    # kanavalla  mailbox[i,j] kulkevat
    sanomat  prosessilta i prosessilta j
    
    
    process P[i=0 to N-1]  {
       int maxlkm           #  vaiheiden
    lukumäärä
       ennuste  arvo[N];
      /* alustukset
         ....
     */
    
    
      for [phase = 1 to maxlkm] {
      /*  laske tämä vaihe
           input: aikaisemmat arvot
           suoritetaan laskenta 
    
           tuloksena  uudet paikalliset
    arvot
     */
     
    
     for [kaikille j: P[j]   on  P[i]:n
    naapuri] 
    
         send mailbox[i, j] (value[i]); 
    
     for [kaikille j: P[j] on P[i]:n
    naapuri]
         receive mailbox[j,i](value[j]);
    
    
       }
     }
    
    
    = = = = = = = = = = = = = = = = = = = = = = = = == = = = = = = = = = = = = = = = = = = = = = = =

    Millaiset kanavat?