modelul calculului concurent - software.ucv.rosoftware.ucv.ro/~cbadica/scd/cap1.pdf · –...
TRANSCRIPT
2018-2019
Modelul calculului concurent
Capitolul 1
2018-2019
Rezumat
• Definirea modelului abstract al calculului
concurent. Modelul intreteserii.
• Corectitudine
• Implementarea modelului in Java si Pthreads.
2018-2019
Calcul concurent
• Program secvential = program “clasic” in care instructiunile masina rezultate in urma compilarii sale sunt executate secvential (“la rand”) de un procesor.
• Program paralel = executiile proceselor componente se suprapun in timp.
• Program concurent = compus din mai multe programe secventiale care pot fi executate in paralel. Programele secventiale din cadrul unui program concurent se numesc si procese (a nu se confunda cu notiunea de proces dintr-un SO).
• Concurenta = paralelism potential in care procesele se pot executa intretesut (engl. interleaved) prin partajarea unui numar restrans de procesoare.
• Calcul concurent = instrument de abstractizare pentru paralelismul potential.
2018-2019
Exemple de concurenta
• Operatii de intrare – iesire
• Multitasking
• Multiprocesoare si multicalculatoare
• Sisteme distribuite
2018-2019
Operatii de intrare – iesire
• Operatiile de intrare – iesire necesita un timp mult mai indelungat de
desfasurare decat operatiile interne de procesare ce folosesc procesorul si
memoria interna.
– Interfetele om – masina implica activitatea umana care este foarte lenta
– Pot necesita operatii mecanice: click-uri cu mouse-ul, folosirea tastaturii
– Pot necesita accesul la dispozitive de memorie externa, cu timpul de acces
mai mare decat timpul intern de procesare
Timp
Procese
Proces I/E
Calcul
Start I/E Sfarsit I/E
• Se folosesc intreruperile
ce permit separarea
conceptuala a procesului
de I/E de procesul de
calcul propriuzis.
• Cele doua procese se pot
executa concurent.
2018-2019
Multitasking
• Multitasking-ul este o functie de baza a nucleului SO.
Permite ca executia proceselor sa fie suprapusa
“virtual” in timp prin intermediul planificatorului
(engl. scheduler) unui SO.
• Planificatorul realizeaza felierea timpului (engl. time
slicing) si controleaza executia echitabila (engl. fair) a
proceselor prin alocarea periodica a procesorului
fiecarui proces din SO.
2018-2019
Multithreading
• Limbajele moderne de programare implementeaza
multitasking-ul in cadrul programelor de aplicatie prin
facilitatea de a defini calcule concurente folosind
conceptul de fir de executie (engl. thread) si
multithreading.
• Programele interactive (cu GUI) trateaza evenimentele
utilizator pe un fir separat ce ruleaza concurent cu firul
alocat programului principal ⇒ GUI-ul implica operatii
de I/E
2018-2019
Multiprocesoare si multicalculatoare
• Calculatoarele moderne sunt multiprocesor, adica contin mai
multe CPU-uri.
– CPU-urile pot sa fie omogene sau specializate: de exemplu pentru
operatii de I/E sau pentru operatii grafice (GPU).
– Un CPU poate contine mai multe unitati de procesare numite si core-
uri, in arhitectura multicore.
• Sistemele de calcul moderne sunt multicalculator, adica sunt
compuse din mai multe calculatoare:
– Retele locale de calculatoare
– Cluster-e de calculatoare
– Centre de date
– Sisteme distribuite de mari dimensiuni: WWW, P2P, sisteme de cloud
computing, etc.
2018-2019
Sisteme distribuite
• Sistemele distribuite sunt sisteme multicalculator eterogene ce
comunica prin schimb de mesaje si urmaresc in primul rand
partajarea resurselor:
– Date
– Servicii
– Largime de banda de retea pentru comunicatie
• Accesul la resursele partajate are loc in mod concurent.
• Cresterea performantelor componentelor de deservire (servere)
dintr-un SD poate face apel la arhitecturi multiprocesor sau
multicore, ce necesita folosirea programarii concurente.
• La randul lor, arhitecturile multiprocesor pot folosi intern
comunicarea prin schimb de mesaje, ce face inevitabil apel la
tehnici specifice SD.
2018-2019
Limite – Legea lui Amdahl
• Exprima limita superioara a vitezei de executie a unui program concurent
functie de cat de mult din program trebuie executat secvential.
• Factorul de accelerare (engl. speedup) al unui program:
𝑆𝑛 = 𝑇1
𝑇𝑛
unde: 𝑇𝑖 = durata executiei programului pe 𝑖 ∈ 1, 𝑛 procesoare,
• Fie un program cu o fractiune 𝑝 ∈ [0,1] ce poate fi executata concurent.
Daca executam programul pe n procesoare, avem:
𝑆𝑛 =1
1– 𝑝 +𝑝𝑛
• Daca 𝑝 < 1 atunci:
𝑆𝑛 <1
1−𝑝 si lim
𝑛→∞𝑆𝑛 =
1
1−𝑝
• Daca 𝑝 = 1 atunci:
𝑆𝑛 = 𝑛
2018-2019
Exemplu – Legea lui Amdahl
• Cazul I: Zugravirea unui apartament cu 5 camere identice de catre 5 zugravi. Se
poate asigna cate un zugrav pe camera, rezultand astfel un factor de accelerare
maxim egal cu 5. In acest caz avem 𝑝 = 1 rezultand:
𝑆5 =1
1 − 1 +15
= 5
• Cazul II: Zugravirea unui apartament cu 5 camere din care 4 camere sunt
identice iar a cincea este dubla (jumatatile necesita zugravirea secventiala), de
catre 5 zugravi. Atunci 𝑝 = 5/6 si 𝑛 = 5 rezultand:
𝑆5 =1
1 −56
+
565
= 3
• Limita superioara a factorului de accelerare este: 1
1 − 𝑝=
1
1 −56
= 6
• Tema: de cati zugravi este nevoie pentru a obtine o accelerare de 4, respectiv 5?
2018-2019
Modelul întreţeserii
• Program concurent = o multime finita de
procese secventiale. Procesele sunt compuse
dintr-o multime finita de instructiuni atomice.
• Executia unui program concurent (numita si
calcul concurent) = o secventa de instructiuni
atomice rezultata prin întreţeserea arbitrara
(engl. arbitrary interleaving) a instructiunilor
atomice ale fiecarui proces component. O
executie (calcul) se numeste si scenariu.
• Fiecare proces component contine un pointer
de control, numit si numarator de program,
care indica urmatoarea instructiune ce va fi
executata din acel proces. Instructiunea
respectiva se numeste activata (engl. enabled).
Sursa: M.Ben-Ari, 2006
2018-2019
Exemplu de întreţesere
• Se considera procesele:
p = instructiunea p1 urmata de instructiunea p2: p1 p2
q = instructiunea q1 urmata de instructiunea q2: q1 q2
• Scenariile posibile sunt:
p1 q1 p2 q2
p1 q1 q2 p2
p1 p2 q1 q2
q1 p1 q2 p2
q1 p1 p2 q2
q1 q2 p1 q2
• Urmatorul scenariu este ilegal (imposibil):
p2 p1 q1 q2
• Tema: cate scenarii exista daca p are m pasi si q are n pasi?
2018-2019
Exemplu trivial de program concurent
integer n 0
p q
integer k1 1
p1: n k1
integer k2 2
p2: n k2
Pseudocod concurent
• Un algoritm contine:
– Titlul
– Declaratiile de variabile globale
– O multime de coloane, cate o coloana pentru fiecare proces
– Fiecare proces incepe cu declaratii de variabile locale
– Urmeaza instructiunile atomice etichetate ale procesului
Sursa: M.Ben-Ari, 2006
2018-2019
Diagrama de stari
• Executia unui program
concurent este definita de o
diagrama cu stari si tranzitii
intre stari.
• O stare include:
– Valorile pointerilor de
control pentru fiecare proces
– Valorile variabilelor globale
si locale din fiecare proces
• Numarul maxim de stari al
unui program concurent
creste exponential cu
numarul de procese. De ce?
p1: n k1
p2: n k2
k1 = 1, k2 = 2
n = 0
(end)
p2: n k2
k1 = 1, k2 = 2
n = 1
p1: n k1
(end)
k1 = 1, k2 = 2
n = 2
(end)
(end)
k1 = 1, k2 = 2
n = 2
(end)
(end)
k1 = 1, k2 = 2
n = 1
Sursa: M.Ben-Ari, 2006
2018-2019
Tranzitii de stare
• Tranzitie: Fie s1 si s2 doua stari ale unei diagrame de stare a
unui program concurent. Exista o tranzitie de la s1 la s2 daca si
numai daca executand o instructiune a unui proces indicata de
pointerul sau de control din s1 ne conduce la starea s2.
• Diagrama de stari: Se construieste recursiv pornind de la
nodul unic ce reprezinta starea initiala s0 a programului.
– Se construiesc tranzitiile posibile din s0 rezultand o multime de stari de
nivel 1.
– Apoi se construiesc toate tranzitiile posibile din starile de nivel 1
rezultand starile de nivel 2, s.a.m.d.
– Se obtine in final diagrama de stare ce contine multimea tuturor
starilor ce pot fi atinse (engl. reachable) pornind din starea initiala.
2018-2019
Procesul p Procesul q n k1 k2
p1: n k1 p2: n k2 0 1 2
(end) p2: n k2 1 1 2
(end) (end) 2 1 2
Scenarii
• Un scenariu se reprezinta printr-un tabel.
• Coloanele reprezinta procesele si variabilele.
• Liniile reprezinta starile.
• Instructiunea executata dintr-o stare se subliniaza.
• Scenariul din exemplu reprezinta calea din partea stanga a diagramei de stari prezentata anterior.
Sursa: M.Ben-Ari, 2006
2018-2019
Intreteserea in sistemele multitasking
• Exista un procesor si o multime de procese, fiecare avand alocat un segment
de memorie. Starea unui proces (stocata in segmentul de memorie alocat)
contine codul său (inclusiv numaratorul de program), datele sale (variabilele)
si valorile registrelor interne.
• Din cand in cand, se declanseaza intreruperi de la dispozitivele de I/E sau de la
expirarea cuantei de timp. O intrerupere determina comutarea contextului
(engl. context switch) adica se schimba procesul din care se va executa
urmatoarea instructiune de catre procesor.
• Intr-o cuanta de timp se executa foarte multe instructiuni, dar momentul
intreruperii este arbitrar, fapt ce justifica intreteserea arbitrara.
Sursa: M.Ben-Ari, 2006
2018-2019
Intreteserea in sistemele multiprocesor
• Daca numarul de CPU-uri este mai mare sau egal cu numarul de procese,
executia intretesuta nu mai este necesara.
• O problema sunt conflictele de memorie (engl. memory contention). Doua
procese incearca sa scrie simultan in aceeasi locatie. Operatiile de scriere
sunt serializate de hardware a.i. rezultatul sa nu fie inconsistent (adica daca
un proces scrie 1 si celalalt 2 sa nu rezulte o valoare nedeterminata, de ex. 3).
Rezultatul va fi oricum nedeterminist, functie de care proces va scrie ultimul,
in urma intreteserii ce apare pentru evitarea conflictului.
Sursa: M.Ben-Ari, 2006
2018-2019
Intreteserea in sistemele distribuite
• Daca fiecare nod executa cate un proces atunci intreteserea nu este necesara.
• Insa, intr-un SD nu exista ceas global, astfel incat procesele ce ruleaza pe
noduri separate nu se pot coordona decat prin schimb de mesaje. Dpdv global
executiile lor apar ca fiind intretesute in mod arbitrar, datorita faptului ca nu
se poate presupune nici un fel de sincronizare a ceasurilor.
• Totodata, durata unui schimb de mesaje nu poate fi teoretic prezisa, ea
depinzand de topologia SD (tipul grafului, de exemplu inel, complet, …),
incarcarea retelei, etc.
Sursa: M.Ben-Ari, 2006
2018-2019
Intretesere arbitrara
• Se poate ignora timpul in analiza programelor concurente.
Astfel, va conta doar ordinea in care sunt executate
instructiunile, nu si momentul de timp cand are loc executia.
• Este posibila construirea de algoritmi care sunt robusti la
modificarea hardware-ului sau a platformei software.
• Este aproape imposibila repetarea executiei unui program
concurent, asa cum se poate face cu un program secvential, in
scopul depanarii acestuia.
• Concluzie: Modelul de intretesere arbitrara ne permite
modelarea si analiza formala a programelor concurente intr-
o maniera independenta de hardware, de platforma software
sau de limbajul de programare.
2018-2019 • Algoritmul este corect in raport cu postconditia n = 2.
Incrementare atomica
integer n 0
p q
p1: n n + 1 q1: n n + 1
Atomicitatea instructiunilor
p q n
p1: nn+1 q1: nn+1 0
(end) q1: nn+1 1
(end) (end) 2
p q n
p1: nn+1 q1: nn+1 0
p1: nn+1 (end) 1
(end) (end) 2
Sursa: M.Ben-Ari, 2006
2018-2019
• Corectitudinea unei analize in presupunerea de intretesere
arbitrara depinde de presupunerea de atomicitate a
instructiunilor – ce instructiuni atomice sunt permise?
Incrementare neatomica
integer n 0
p q
integer temp
p1: temp n
p2: n temp+1
integer temp
q1: temp n
q2: n temp+1
Incrementare neatomica
Sursa: M.Ben-Ari, 2006
2018-2019
p q n p.temp q.temp
p1: temp n q1: temp n 0 ? ?
p2: n temp+1 q1: temp n 0 0 ?
(end) q1: temp n 1 0 ?
(end) q2: n temp+1 1 0 1
(end) (end) 2 0 1
p q n p.temp q.temp
p1: temp n q1: temp n 0 ? ?
p2: n temp+1 q1: temp n 0 0 ?
p2: n temp+1 q2: n temp+1 0 0 0
(end) q2: n temp+1 1 0 0
(end) (end) 1 0 0
Incorectitudinea incrementarii atomice
Sursa: M.Ben-Ari, 2006
2018-2019
Instructiuni cod masina
• Algoritmul “Incrementare neatomica” poate rezulta in
urma compilarii in limbaj cod masina a algoritmului
“Incrementare atomica”.
• Instructiunile din limbajul de programare sursa sunt
compilate in instructiuni masina (fizica sau virtuala), in
functie de tipul de arhitectura al masinii:
– Masini cu registre generale (de exemplu PDP-11, ARM)
– Masini stiva (de exemplu JVM, x86 FP)
• Concluzie: compilarea codului unui algoritm poate
afecta proprietatile (de corectitudine) ale algoritmului
2018-2019
• Tema: De gasit un scenariu pentru care la sfarsitul
executiei acestui algoritm avem n 2.
Incrementare pe masina cu registre generale
integer n 0
p q
p1: load R1,n
p2: add R1,#1
p3: store n,R1
q1: load R1,n
q2: add R1,#1
q3: store n,R1
Incrementare pe masina cu registre generale
Sursa: M.Ben-Ari, 2006
2018-2019
• Tema: De gasit un scenariu pentru care la sfarsitul
executiei acestui algoritm avem n 2.
Incrementare pe masina stiva
integer n 0
p q
p1: push n
p2: push #1
p3: add
p4: pop n
q1: push n
q2: push #1
q3: add
q4: pop n
Incrementare pe masina stiva
Sursa: M.Ben-Ari, 2006
2018-2019
• Instructiunea q1 se poate intretese oriunde intre instructiunile lui p.
Optimizarea codului (valoarea lui n dupa p1 se poate pastra intr-un
registru) poate cauza ca l sa nu primeasca cea mai recenta valoare a lui n.
• Fortarea stocarii imediate in memorie a valorii lui n se poate face
declarand n ca fiind variabila volatile.
Variabile volatile
integer n 0
p q
integer l1,l2
p1: n expresie
p2: calcul ce nu foloseste n
p3: l1 (n+1)*2
p4: l2 n+1
p5: n l1*l2
integer l
q1: l n+3
Variabile volatile
Sursa: M.Ben-Ari, 2006
2018-2019
Corectitudine
• Corectitudinea unui program secvential presupune:
– (i) terminarea programului si
– (ii) corectitudinea rezultatului (verificare postconditie)
• Corectitudinea unui program concurent presupune
verificarea unor proprietati ale tuturor scenariilor
posibile rezultate prin intreteserea arbitrara.
• Observatie: deoarece corectitudinea se defineste
pentru TOATE scenariile posibile, demonstrarea sa
prin testarea programului este in general nefezabila !
2018-2019
Proprietati de siguranta
• Proprietati de siguranta (engl. safety). O proprietate de
siguranta 𝑃 este satisfacuta daca si numai daca pentru
orice scenariu posibil si pentru orice stare ea este
adevarata.
• Exemplu:
“Intotdeauna este afisat cursorul mouse-ului”.
“Intotdeauna in operatiile de impartire impartitorul
este nenul”
2018-2019
Proprietati de vivacitate
• Proprietati de vivacitate (engl. liveness). O proprietate
de vivacitate 𝑃 este satisfacuta daca si numai daca
pentru orice scenariu posibil exista o stare in care ea
este adevarata.
• Exemplu:
“Daca se realizeaza un click cu butonul mouse-ului atunci
eventual cursorul mouse-ului isi va schimba forma”
2018-2019
Dualitate
• Proprietatile de siguranta se exprima prin sablonul:
“Intotdeauna ceva rau nu se va intampla”
• Proprietatile de vivacitate se exprima prin sablonul:
“Eventual ceva bun se va intampla”
• Dorim sa proiectam programe concurente care realizeaza ceva util
(proprietate de vivacitate), fara sa violeze proprietatile de siguranta.
• O proprietate de siguranta se exprima logic prin s P(s), unde
predicatul P este definit prin: P(s) “ceva rau se intampla in starea s”.
• O proprietate de vivacitate se exprima prin s P(s), unde predicatul P este
definit prin: P(s) “ceva bun se intampla in starea s”.
• Proprietatile programelor concurente se exprima natural in logica
temporala.
2018-2019
Echitate • Exista o singura exceptie a presupunerii ca orice intretesere arbitrara
reprezinta o executie valida a unui program concurent.
• Un scenariu este (slab) echitabil (engl. weakly fair) daca: “pentru
orice stare a sa s daca incepand de la s incolo o instructiune i este
activata in mod continuu atunci eventual i va aparea in scenariu,
adica va fi executata !”
• Cu alte cuvinte echitatea ne asigura ca: “daca incepand de la un
moment dat arbitrar o instructiune devine si ramane activata ea va fi
sigur executata la un moment dat in viitor.”
• Sa presupunem ca am executat un scenariu s0, s1, …, si si ca incepand
cu si, pointerul de control al procesului p indica spre instructiunea pj ce
este astfel activata continuu. Atunci pj eventual va aparea (va fi
selectata) in acest scenariu pentru o stare sk, k i.
• Observatie: atribuirile si instructiunile de control odata activate, vor
ramane activate in mod continuu.
2018-2019
• Proprietatea “Intotdeauna acest algoritm se opreste” este falsa deoarece se
poate construi scenariul infinit: p1, p2, p1, p2, p1, p2, …
• Insa instructiunea q1 a procesului q este activata in mod continuu. Daca se
accepta doar scenariile slab echitabile atunci acest scenariu infinit trebuie
sa contina si instructiunea q1. Aceasta instructiune determina ca flag = true
sa fie adevarata, astfel ca se iese din bucla si scenariul se termina.
Oprire prin iesire din bucla
integer n 0
boolean flag false
p q
p1: while flag = false
p2: n 1 – n
q1: flag true
Oprire in conditii echitabile
Sursa: M.Ben-Ari, 2006
2018-2019
Instrumente pentru programare concurenta
• Limbaje de programare concurenta:
– Ada
– Modula
– Pascal concurent
• Biblioteci si API-uri:
– Concurenta din Java
– Concurenta in C#. Modelul este similar cu Java
– Biblioteca de fire POSIX Pthreads – API standard IEEE pentru limbajul C
• Instrumente de simulare si verificare:
– Limbajul de modelare Process Meta Language – Promela
– Instrumentul SPIN de verificare a modelelor
2018-2019
Noi arhitecturi, limbaje si biblioteci pentru concurenta
• Arhitectura “shared-nothing”: arhitectura distribuita de calcul
compusa din noduri independente a.i. sa nu contina nici un punct
de conflict privind accesul la memoria interna sau externa.
• Aceste arhitecturi evita partajarea memoriei comune si folosirea
zavoarelor, facand apel la noi algoritmi de concurenta fara
blocare (engl. non-blocking concurrency algorithms).
• Vert.x: set de instrumente (biblioteca + platforma) poliglot
pentru construirea de aplicatii reactive pe JVM. – Se poate folosi ca biblioteca integrata in aplicatii Java
– Se poate folosi ca platforma (ca un fel de server). Odata pornita, se pot
lansa diverse componente de la linia de comanda.
– Reactiv: componente controlate de evenimente ce comunica prin mesaje.
– Poliglot: permite scrierea de cod in limbaje de programare multiple:
Java, JavaScript, Ruby, Python si Groovy.
2018-2019
Ce sunt firele?
• Un fir (engl. thread) este o resursa de procesare care poate
executa o sarcina (aplicatie) (engl. application task) pe un
calculator (procesor) gazda.
• Firele permit implementarea unor fluxuri de control concurent
in cadrul aceluiasi proces. Firele unui proces pot partaja date.
Fiecare fir se caracterizeaza prin propriul numarator de
program, stiva si multime de variabile locale.
• Firele se mai numesc si procese de categorie usoara (engl.
lightweight processes). Sistemele de operare moderne trateaza
firele ca unitati de baza in operatiile de planficare.
• Firele unui proces partajeaza spatiul adreselor procesului, ele
putand comunica prin date partajate. Partajarea datelor poate
crea conflicte de acces la memorie cu rezultate imprevizibile.
2018-2019
Concurenta in Java
• Platforma Java a fost dezvoltata pentru a facilita
programarea concurenta.
• Incepand cu versiunea 5.0 ea include o interfata API de
nivel-inalt pentru concurenta.
• Programarea concurenta presupune folosirea proceselor si
firelor. Un proces poate contine unul sau mai multe fire de
executie. De obicei un JVM ruleaza ca un singur proces.
• O facilitate esentiala a platformei Java este programarea
multi-fir – multithreaded.
• Din punctul de vedere al programatorului, orice aplicatie
Java are alocat un proces si ea contine cel putin un fir
numit firul principal al aplicatiei.
2018-2019
Fire de executie
Sursa: Oaks & Wong, 2009
2018-2019
Clasa Thread
• Un fir Java este reprezentat printr-un obiect java.lang.Thread. public class MyThread extends Thread {
String message;
public MyThread(String m) {
message = m;
}
public void run() {
System.out.println(message);
}
}
public class MainThread {
public static void main(String args[]) {
(new MyThread("Hello world from thread")).start();
}
}
• Sarcina firului se defineste prin rescrierea metodei run.
• Pornirea firului se face cu metoda start.
2018-2019
Interfata Runnable
• Un fir Java este reprezentat printr-un obiect ce implementeaza
interfata java.lang.Runnable. public class MyRunnableThread implements Runnable {
public void run() {
System.out.println(
"Hello world from runnable thread");
}
}
public class MainRunnableThread {
public static void main(String args[]) {
(new Thread(new MyRunnableThread())).start();
}
}
• Sarcina firului se defineste tot prin rescrierea metodei run.
• Abordarea cu Runnable este mai generala deoarece firul poate
mosteni o alta clasa, Runnable fiind o interfata.
2018-2019
Fire concurente public class MainMoreThreads {
public static void main(String[] args) {
Thread[] thread = new Thread[8];
for (int i = 0; i < thread.length; i++) {
thread[i] =
new MyThread("Hello world from thread" + i);
}
for (int i = 0; i < thread.length; i++) {
thread[i].start();
}
System.out.println("Main finished");
}
}
• Tema: sa se ruleze acest program Java. Ce se observa?
2018-2019
Metoda join public class MainMoreThreadsJoin {
public static void main(String[] args) {
Thread[] thread = new Thread[8];
for (int i = 0; i < thread.length; i++) {
thread[i] =
new MyThread("Hello world from thread" + i);
}
for (int i = 0; i < thread.length; i++) {
thread[i].start();
}
for (int i = 0; i < thread.length; i++) {
try {
thread[i].join();
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Main finished");
}
}
• Metoda join permite
unui fir t1 ce executa
apelul t2.join() sa se
suspende pana cand
firul t2 se termina.
Apoi t1 isi continua
executia.
• In exemplu, firul
principal asteapta ca
toate firele sa se
termine, apoi
afiseaza mesajul
final si se termina.
2018-2019
Oprirea unui fir
• Obs: NU se folosesc metodele depreciate stop, resume si suspend !
• Cat timp un fir este activ metoda isAlive intoarce true.
• Pentru ca un fir sa se termine, metoda sa run trebuie implementata
astfel incat sa-si termine executia. Se poate folosi un flag astfel: public class FirulMeu extends Thread {
...
private volatile boolean done = false;
...
public void run( ) {
while (!done) {
...
}
}
public void setDone() {
done = true;
}
}
2018-2019
Intreruperea unui fir
• Metoda de oprire cu flag are dezavantajul ca poate exista o intarziere
(uneori arbitrar de mare) intre momentul setarii flagului si momentul
testarii conditiei de terminare a metodei run. In aceste cazuri se poate
realiza intreruperea firului.
• Intreruperea se realizeaza apeland metoda interrupt a firului. Ea are ca
efect generarea unei exceptii InterruptedException ce provoaca iesirea din
metoda run si tratarea exceptiei.
• In plus, ea seteaza un flag in fir, ce poate fi testat cu metoda isInterrupted: public class FirulMeu extends Thread {
...
public void run( ) {
while (!isInterrupted()) {
...
}
}
}
2018-2019
Suspendarea unui fir
• Un fir isi poate suspenda executia pentru o durata de timp
predefinita folosind metoda statica sleep. Durata de timp se
specifica printr-un numar intreg de ms optional plus un numar
intreg de ns. public static void sleep(long millis);
public static void sleep(long millis, int nanos);
• Metoda se foloseste de obicei pentru autosuspendarea unui fir.
In implementarile curente ale limbajului Java rezolutia duratei
de suspendare este de ordinul ms.
• Exemplu:
Thread.sleep(1000); // pause for 1 second = 1000 ms
2018-2019
Exemplu cu fire
• Se creaza un fir pentru care realizeaza in mod repetat
numararea periodica descrescatoare.
• La fiecare decrementare genereaza un sunet tick.
• La fiecare trecere prin zero genereaza un sunet beep.
• Firul dispune de o interfata grafica pe care se afiseaza valoarea
contorului la fiecare decrementare.
• La inchiderea interfeetei grafice, metoda run se termina si
astfel incat executia firului inceteaza.
• Firul dispune de o variabila de “stare” care reprezinta valoarea
curenta a contorului.
• Exemplul o adaptare dupa Kramer & Magee, 2006.
2018-2019
Variabile locale
public class CountDown extends Frame implements Runnable {
Thread counterThread;
int i;
final static int N = 4;
AudioClip beepSound, tickSound;
NumberCanvas display;
volatile boolean done = false;
• Clasa NumberCanvas implementeaza o panza de desen –
Canvas unde se afiseaza valoarea numaratorului. Aceasta
valoare se seteaza din aplicatie cu metoda setvalue.
2018-2019
Initializare public CountDown() {
add(display = new NumberCanvas("CountDown"));
try {
File tickFile = new File("...\\tick.au");
File beepFile = new File("...\\beep.au");
tickSound = Applet.newAudioClip(
new URL(tickFile.toURI().toURL().toString()));
beepSound = ...
}
catch (MalformedURLException mfe){
System.out.println("An error occured, please try again...");
}
counterThread = new Thread(this);
i = N;
counterThread.start();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { terminate(); }
});
}
2018-2019
Corpul firului. Oprirea firului
public void run() {
while(! done) {
if (i>0) {
tick();
--i;
}
if (i==0) {
beep();
i = N;
}
}
}
private void tick() {
display.setvalue(i);
tickSound.play();
try {
Thread.sleep(1000);
}
catch(
InterruptedException e) {}
}
private void tick() { ... }
public void terminate() {
done = true;
setVisible(false);
dispose();
}
2018-2019
Programul principal
public static void main(String[] args) {
CountDown fereastra = new CountDown();
fereastra.setTitle("Count Down");
fereastra.setSize(150,200);
fereastra.setVisible(true);
}
2018-2019
NumberCanvas – Variabile & constructor package display;
import java.awt.*;
public class NumberCanvas extends Canvas {
int value_ = 0;
String title_;
Font f1 = new Font("Helvetica",Font.BOLD,36);
Font f2 = new Font("Times",Font.ITALIC+Font.BOLD,24);
public NumberCanvas(String title) {
this(title,Color.cyan);
}
public NumberCanvas(String title,Color c) {
super();
title_ = title;
setBackground(c);
}
2018-2019
NumberCanvas – Afisari public void setcolor(Color c){
setBackground(c);
repaint();
}
public void setvalue(int newval) {
value_ = newval;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
// Display the title
g.setFont(f2);
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(title_);
int h = fm.getHeight();
int x = (getSize().width - w)/2;
int y = h;
g.drawString(title_, x, y);
g.drawLine(x,y+3,x+w,y+3);
// Display the value
g.setFont(f1);
fm = g.getFontMetrics();
String s1 =
String.valueOf(value_);
w = fm.stringWidth(s1);
h = fm.getHeight();
x = (getSize().width - w)/2;
y = (getSize().height+ h)/2;
g.drawString(s1, x, y);
}}
2018-2019
Cate fire creaza aceasta aplicatie ?
• Se poate folosi metoda:
currentThread()
a clasei Thread pentru a determina si afisa informatii despre
firul care ruleaza in mod curent.
• Aplicatia prezentata creaza 3 fire, si anume:
– Firul principal al aplicatiei
– Firul creat explicit de aplicatie pentru implementarea numaratorului
– Firul pe care se trateaza evenimentele GUI-ului
• Tema: sa se afiseze informatii referitoare la aceste fire folosind
metoda currentThread.
2018-2019
Concurenta folosind biblioteca Pthreads
• POSIX Threads, cunoscut si sub numele de Pthreads, defineste un mod de
executie concurenta independent de un anumit limbaj de programare.
• Modelul Pthreads este implementat fie in nucleul unui sistem de operare
conform standardului POSIX, fie ca biblioteca separata.
• Pthreads poate fi preemptiv sau nepreemptiv (engl. preemptive sau
nonpreemptive).
• In acest context, preemtiunea (sau preemptiunea?) se refera la intreruperea
temporara a unui proces (fir sau task), nefiind necesara cooperarea din
partea sa (cooperare = cedare explicita a controlului), cu intentia de a
relua executia procesului la un moment ulterior. Sarcina preemtiunii
revine planficatorului (preemptiv) (engl. scheduler). Acesta poate
intrerupe procesul curent astfel incat sa aloce controlul executiei altui
proces din sistem, conform unui algoritm de planificare (engl.scheduling).
• Pthreads este disponibila in C => pentru compilare se recomanda gcc.
2018-2019
Crearea unui fir in Pthreads
int pthread_create (
pthread_t* thread_id,
const pthread_attr_t* attributes,
void* (*thread_function)(void*),
void* argument);
• thread_id = parametru ce intoarce identificatorul firului creat
• attributes = adresa unei inregistrari ce contine caracteristicile firului creat.
Se poate folosi NULL pentru folosirea unor atribute implicite.
• thread_function = pointer la functia ce descrie codul firului
• argument = pointer generic catre argumentele firului.
2018-2019
Apelul join() in Pthreads
int pthread_join (
pthread_t thread,
void** status_ptr);
• thread = parametru ce reprezinta identificatorul unui fir. Firul ce executa
acest apel se suspenda pana cand firul desemnat prin thread se termina.
• status_ptr = parametru ce primeste adresa rezultatului intors de firul
thread, la terminarea sa. In acest fel firul ce executa apelul va continua
preluand rezultatul firului thread care s-a terminat..
• Observatie: acest apel este asemanator ca semantica metodei join() din
clasa Thread.
2018-2019
Exemplu cu Pthreads - I
#include <stdio.h>
#define HAVE_STRUCT_TIMESPEC
#include <pthread.h>
#define NUM_THREADS 8
void* hello(void* arg) {
printf("Hello from thread %i\n", (int)(*arg));
}
2018-2019
Exemplu cu Pthreads - II
int main() {
pthread_t thread[NUM_THREADS];
int i;
for (i = 0; i < NUM_THREADS; i++) {
if ( pthread_create(&thread[i], NULL, hello, (void*)i) != 0 ) {
printf("pthread_create() error\n");
return 1;
}
}
for (i = 0; i < NUM_THREADS; i++) {
pthread_join(thread[i], NULL);
}
return 0;
}
2018-2019
Compilare cu GCC
gcc -o hello hello.c -pthread
Optiune pentru specificarea numelui fisierului executabil
Numele fisierului executabil
Numele fisierului C sursa
Specificarea bibliotecii
2018-2019
Tema
• Sa se determine toate numerele prime din intervalul [1, 𝑛] folosind 𝑘 fire. Fie 𝑛 = 𝑘𝑞 + 𝑟 unde 0 ≤ 𝑟 < 𝑘 unde 𝑟 este
restul impartirii lui 𝑛 la 𝑘. Se vor considera 2 solutii:
– Se partitioneaza intervalul [1, 𝑛] in 𝑘 intervale astfel: 𝐼1 = [1, 𝑞 + 1], 𝐼2 = [𝑞 + 2, 2𝑞 + 2], …, 𝐼𝑟 = [ 𝑟 − 1 𝑞 + 𝑟, 𝑟𝑞 + 𝑟], 𝐼𝑟+1 = [𝑟𝑞 +𝑟 + 1, 𝑟 + 1 𝑞 + 𝑟], …, 𝐼𝑘 = [ 𝑘 − 1 𝑞 + 𝑟 + 1, 𝑘𝑞 + 𝑟]. Fiecare fir
1 ≤ 𝑗 ≤ 𝑘 va determina numerele prime din intervalul 𝐼𝑗.
– Multiplii lui 𝑘 + 1 strict mai mari ca 𝑘 + 1 nu sunt numere prime. Se
elimina aceste numere din intervalul [1, 𝑛] rezultand multimea 𝑀.
Aceasta multime se partitioneaza in 𝑘 submultimi astfel: pentru fiecare
1 ≤ 𝑗 ≤ 𝑘 multimea 𝑀𝑗 contine acele elemente din 𝑀 care dau restul 𝑗
la impartirea prin 𝑘 + 1. Totodata se considera ca 𝑘 + 1 ∈ 𝑀1. Fiecare
fir 𝑗 va determina numerele prime din multimea 𝑀𝑗.
Care dintre cele doua solutii este mai buna si de ce?
2018-2019
• Ce valori poate avea variabila n la sfarsitul executiei
algoritmului de numarare concurenta?
• Tema: sa se implementeze acest algoritm in Java. Ce valori se
obtin pentru n la final?
Numarare concurenta
integer n 0
p q
integer temp
p1: do 10 times
p2: temp n
p3: n temp + 1
integer temp
q1: do 10 times
q2: temp n
q3: n temp + 1
Tema – numarare concurenta
Sursa: M.Ben-Ari, 2006