Differences

This shows you the differences between two versions of the page.

Link to this comparison view

apd:laboratoare:05 [2021/10/22 16:19]
florin.mihalache [Căutarea binară paralelă]
apd:laboratoare:05 [2023/10/08 16:32] (current)
dorinel.filip move
Line 1: Line 1:
 ===== Laboratorul 5 - Probleme de sincronizare ===== ===== Laboratorul 5 - Probleme de sincronizare =====
  
-==== wait, notify & notifyAll ==== +Documentația de laborator ​s-a mutat la [[https://mobylab.docs.crescdi.pub.ro/​docs/​parallelAndDistributed/introduction|această adresă]].
- +
-Sa presupunem ca avem 2 thread-uri: thread-ul 0 care calculeaza o valoare si o plaseaza in variabila `a` si thread-ul 1 care atunci cand variabila `a` se actualizeaza,​ o afiseaza in interfata grafica a unui program. +
-O posibila implementare pentru functia thread-ului 0, in pseudocod, ar fi: +
- +
-  function_thread_0:​ +
-    noua_valoare = calculeaza_valoare_a() +
-    mutex_a.lock() +
-        a = noua_valoare +
-    mutex_a.unlock() +
- +
- +
-O posibila implementare pentru functia thread-ului 1, in pseudocod, ar fi: +
- +
-  function_thread_1:​ +
-    while(a nu s-a actualizat):​ +
-        mutex_a.lock() +
-        if (a s-a actualizat):​ +
-            afiseaza(a) +
-            a = -1 //  Resetam valoarea lui a. +
-        mutex_a.unlock() +
- +
- +
- +
-Observam o problema cu aceasta abordare: thread-ul 1 va cicla in bucla while de mai multe ori, chiar daca thread-ul 0 nu-l actualizeaza pe `a`, ocupand inutil timp pe procesor. +
-Aceasta abordare poarta numele de **busy waiting**. Desi **busy waiting** nu este intotdeauna o idee rea, in cazul de fata, prespunuem ca pentru a calcula noua valoarea a lui a, thread-ului 0 ii ia un timp indelungat. Astfel, exista o solutie mai buna oferita ​de primitevele Java **wait()**, **notify()** și **notifyAll()**.  +
- +
-**wait()** - forțează thread-ul curent să intre în așteptare până când alt thread apelează **notify()** sau **notifyAll()** pe același obiect. Pentru ca aceasta să se întâmple, thread-ul curent trebuie să dețină monitorul obiectului respectiv. Deținerea monitorului se poate îmtâmpla în următoarele situații:​ +
-  * s-a executat o metodă synchronized pe obiectul respectiv +
-  * s-a executat un bloc synchronized pe obiectul respectiv +
-  * s-a executat o metodă synchronized statică pe clasa din care face parte obiectul respectiv +
- +
-<note important>​Evitați folosirea **wait()** asupra obiectelor cu vizibilitate globală (statice) sau asupra String-urilor cu valoare constanta (de ex: ''​String myMonitorObject = "";''​),​ deoarece JVM optimizează intern accesul ​la astfel de variabile, având doar o singură instanță în cadrul programului.</​note>​ +
- +
-**notify()** - alege aleatoriu un thread care așteaptă (a apelat **wait()**) monitorul obiectului respectiv și trece-l din starea de //waiting// în starea de //​running//​ +
- +
-**notifyAll()** - trece toate thread-urile care așteaptă (au apelat **wait()**) monitorul obiectului respectiv și trece-le din starea de //waiting// în starea de //​running//​ +
- +
-{{:​apd:​laboratoare:​wait_notify.pdf|CheatSheet Wait-Notify}} +
- +
-==== Semafoare ==== +
- +
-Un semafor poate fi văzut ca un lock ce permite mai multor thread-uri să coexiste într-o anumită regiunie critică la un moment dat. Semaforul folosește un contor care determină câte thread-uri mai pot intra. Odată ajuns la semafor, un thread este lăsat să intre doar dacă numărul de thread-uri aflate în zona critică este mai mic decât numărul maxim de thread-uri setat la crearea semaforului. +
- +
-Pe un semafor se pot realiza următoarele operații:​ +
-  * **acquire** - se încearcă trecerea de semafor; dacă numărul de thread-uri aflate în zona critică este mai mic decât numărul maxim de thread-uri acceptate, thread-ul poate intra +
-  * **release** - un thread apeleză metoda pentru a anunța faptul că și-a terminat treaba în zona critică, permițând astfel incrementarea numărului de thread-uri car pot intra în zona critică +
- +
-<code java> +
-class Something { +
- +
-    static int resource = 0; +
-+
- +
-public class MySemaphore extends Thread { +
- +
-    private int id; +
-    private Semaphore sem; +
- +
-    public MySemaphore(int id, Semaphore sem) { +
-        this.id = id; +
-        this.sem = sem; +
-    } +
-     +
-    public static void main(String args[]) { +
-        Semaphore sem = new Semaphore(1); ​           +
-        MySemaphore mt1 = new MySemaphore(0,​ sem);  +
-        MySemaphore mt2 = new MySemaphore(1,​ sem);  +
-           +
-        mt1.start();​  +
-        mt2.start();​  +
-         +
-        try {  +
-            mt1.join();​ +
-            mt2.join();  +
-        } catch (InterruptedException ex) { +
-            Logger.getLogger(MySemaphore.class.getName()).log(Level.SEVERE,​ null, ex); +
-        } +
-                   +
-        System.out.println("​count" + Something.resource);​  +
-    } +
- +
-    @Override +
-    public void run() { +
-        switch (this.id) { +
-            case 0: +
-                System.out.println("​Starting thread " + id); +
-                try { +
-                    System.out.println("​Thread " + id + " is waiting for a permit."​);​ +
-                    sem.acquire();​ +
-                    System.out.println("​Thread " + id + " gets a permit."​);​ +
- +
-                    for (int i = 0; i < 5; i++) { +
-                        Something.resource++;​ +
-                        System.out.println("​Thread " + id + ": " + Something.resource);​ +
-                        Thread.sleep(10);​ +
-                    } +
-                } catch (InterruptedException exc) { +
-                    System.out.println(exc);​ +
-                } +
- +
-                System.out.println("​Thread " + id + " releases the permit."​);​ +
-                sem.release();​ +
-                break; +
-            case 1: +
-                System.out.println("​Starting thread " + id); +
-                try { +
-                    System.out.println("​Thread " + id + " is waiting for a permit."​);​ +
-                    sem.acquire();​ +
-                    System.out.println("​Thread " + id + " gets a permit."​);​ +
- +
-                    for (int i = 0; i < 5; i++) { +
-                        Something.resource--;​ +
-                        System.out.println("​Thread " + id + ": " + Something.resource);​ +
-                        Thread.sleep(10);​ +
-                    } +
-                } catch (InterruptedException exc) { +
-                    System.out.println(exc);​ +
-                } +
- +
-                ​// Release the permit +
-                System.out.println("​Thread " + id + " releases the permit."); +
-                sem.release();​ +
-                break; +
-        } +
-    } +
- +
-    public int getThreadId() { +
-        return this.id; +
-    } +
- +
-+
-</code> +
- +
-În codul de mai sus se creează un semafor ce acceptă un singur thread în zona critică (observați forma constructorului Semaphore). Practic, acest semafor este similar cu un lock. Semaforul se creează in main si se trimite ca parametru în constructorul thread-urilor. În cadrul metodei run, thread-urile încearcă să apeleze **acquire**,​ însă doar unul dintre ele va reuși să intre în zona de după semafor. Ulterior, după ce se realizează operațiile din zona critică, thread-ul respectiv anunță faptul că a ieșit prin intermediul unui apel de **release**. Observați faptul că, în funcție de thread-ul care reușește să intre în zona respectivă,​ contorul poate scădea până la -5, urmând să revină la 0 apoi, sau să fie crescut până la 5, urmând să ajungă la 0 apoi. +
- +
-{{:​apd:​laboratoare:​semaphore.pdf|CheatSheet Semafoare}} +
- +
-===== Probleme clasice de sincronizare ===== +
- +
-==== Producător - Consumator ==== +
- +
-Problema se referă la două thread-uri: producător și consumator. Producătorul inserează date într-un buffer, iar consumatorul extrage date din acel buffer. Buffer-ul are o dimensiune prestabilită,​ astfel că: +
-  * producătorul nu poate insera date dacă buffer-ul este plin +
-  * consumatorul nu poate extrage date dacă buffer-ul este gol +
-  * producătorul și consumatorul nu pot acționa simultan asupra buffer-ului +
- +
-O implementare corectă a problemei presupune asigurarea faptului că nu vor exista situații de deadlock, adică situații în care cele două thread-uri așteaptă unul după celălalt, neexitând posibilitatea de a se debloca. +
- +
-{{:​apd:​laboratoare:​producer-consumer.pdf|CheatSheet Producator-Consumator}} +
- +
-==== Problema filozofilor ==== +
- +
-Problema se referă la mai mulți filozofi (thread-uri) așezați la o masă circulară. Pe masă se află 5 farfurii și 5 tacâmuri, astfel încât fiecare filozof are un tacâm în stânga și unul în dreapta lui. În timp ce stau la masă, filozofii pot face două acțiuni: mănâncă sau se gândesc. Pentru a mânca, un filozof are nevoie de două tacâmuri (pe care le poate folosi doar dacă nu sunt luate de către vecinii săi). Rezolvarea trebuie să aibă în vedere dezvoltarea unui algoritm prin care să nu se ajungă la un deadlock (situația în care fiecare filozof ține câte un tacâm în mână și așteaptă ca vecinul să elibereze celălalt tacâm de care are nevoie). +
- +
-{{:​apd:​laboratoare:​dining-philosophers.pdf|CheatSheet Problema filozofilor}} +
- +
- +
-===== Exerciții ===== +
- +
-{{:​apd:​laboratoare:​lab05_skel.zip|Schelet laborator 5}} +
- +
-  - Pornind de la scheletul **oneProducerOneConsumer** furnizat, implementați algoritmul Producer-Consumer pentru un buffer de dimensiune 1. **[2P]** +
-  - Modificați algoritmul Producer-Consumer astfel încât să accepte mai mulți producători și mai mulți consumatori. De asemenea, modificați buffer-ul astfel încât să fie de dimensiune > 1. Porniți de la scheletul **multipleProducersMultipleConsumersNBuffer**. **[3P]** +
-  - Rezolvați problema din algoritmul filozofilor (pachetul **philosophersProblem**) și explicați-o**[2P]** +
-  - Implementați căutarea binară paralelă. **[3P]** +
- +
-<note important>​Exercițiile din cadrul acestui laborator trebuie rezolvate folosind mecanisme de sincronizare precum primitivele wait/​notify/​notifyAll sau semafoare. Nu se accepta soluții ce folosesc obiecte concurente.</​note>​+
apd/laboratoare/05.1634908754.txt.gz · Last modified: 2021/10/22 16:19 by florin.mihalache
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0