CATEGORII DOCUMENTE |
Programare in timp real - Limbaje timp -real si notiuni de programare concurenta
1. Generalitati
Un program concurent este compus din mai multe parti care sunt executate concurent, adica exista durate in care sunt mai multe activitati executabile. Programele concurente pot fi implementate folosind limbaje de programare care accepta programarea concurenta cum ar fi ADA, Modula-2, Occam si Java, sau folosind un sistem de operare care ofera facilitati multitasking (de exemplu sistemul de operare UNIX, QNX).
Un proces al unui program concurent trebuie vazut ca o secventa de program care este executata efectiv asincron si prin urmare, nu se poate prezice;
Nedeterminismul referitor la temporizarile relative ale proceselor face ca depanarea si testarea programelor concurente sa fie mult mai dificile decat cele ale programelor secventiale.
Programele concurente pot fi implementate pe sisteme cu:
Cooperarea proceselor consta din:
Cele mai importante probleme ridicate de executia concurenta a programelor sunt:
1. Executia concurenta pe un sistem monoprocesor care implica:
2. Excluderea mutuala care cere ca doua parti concurente ale unui program sa nu fie executate concurent. Aceste parti se numesc sectiini critice, alt proces trebuie sa fie impiedicat sa treaca la executia sectiunii sale critice pana cand primul nu iese din executia sectiunii critice.
Se poate defini o clasa a sectiunii critice care consta dintr-un set de sectiuni critice care trebuie sa fie executate prin excludere mutuala cu alte sectiuni din aceeasi clasa, fara insa a fi necesara executia prin excludere mutuala cu alte sectiuni critice din alte clase.
3. Problema producator-consumator implica existenta a doua procese. Procesul producator genereaza date pe care le transmite procesului consumator. Se cere ca:
4. Problema cititori si scriitor implica existenta unui fisier de date in care un proces scriitor depune date. Iar procesele cititoare le preiau de acolo. Se cere :
5. Problema cititori si scriitori poate fi o generalizare a problemei precedente prin existenta mai multor taskuri scriitoare. Cerintele de sincronizare contsau din :
6. Problema utilizarii in comun a resurselor consta din excluderea mutuala a proceselor de la utilizarea simultana a unor resurse.
Moduri de implementare a concurentei
Programele concurente pot fi implementate folosind:
La randul lor mecanismele software pot fi realizate in :
Cele mai cunoscute mecanisme hardware pentru implementarea concurentei sunt :
Rezervarea accesului la memorie se face in multe sisteme de calcul pe pagini sau pe zone de memorie. In acest curs vor fi prezentate numai facilitatile de programare concurenta oferite- de catre cele mai cunoscute limbaje de programare.
Cerintele unui limbaj de timp-real de nivel superior
Spre deosebire de limbajele de asamblare, nu toate limbajele de programare de nivel superior pot fi utilizate eficient pentru implementarea unor aplicatii de timp-real.
Printre cele mai importante cerinte care trebuie luate in considerare la alegerea unui limbaj de programare pentru implementarea aplicatiilor de timp-real sunt:
Printre cele mai cunoscute limbaje utilizate pentru aplicatiile de timp-real sunt:
2. Regiuni critice
C A. R. Hoare a propus o constructie (specifica limbajelor de programate de nivel inalt)
with v do Q,
prin care se simplifica utilizarea semafoarelor, v reprezinta o variabila declarata comuna
var v : shared T;
T este tipul variabilei v, iar Q reprezinta o secventa critica de instructiuni. In acest mod, regiunile critice la care se refera variabila v se vor exclude reciproc in timp. Utilizand aceasta constructie se evita necesitatea incadrarii sectiunii critice cu primitivele P sl V, sau wait si set (daca se lucreaza cu variabile eveniment).
Regiunile critice pot fi realizate si cu mecanismele din nucleele sistemelor de operare multitasking. Implementarea regiunilor critice este simpla. Declararea variabilei v ca variabila comuna implica, de fapt, construirea unui semafor, sau a unei variabile de tip eveniment.
O dezvoltare a constructiei de mai sus s-a materializat prin regiunile critice conditionate:
with v then B do Q:
unde B este o expresie logica. Executia secventei critice Q este conditionata, in plus fata de cazul precedent, de faptul ca B trebuie sa aiba valoarea logica. ■adevarat (true). Daca dupa intrarea in regiunea critica, aceasta conditie nu este indeplinita, atunci tascul este trecut in asteptare pana la indeplinirea ei.
Si aceasta constructie poate fi realizata cu primitivele nucleului unui system de operare multitasking. Pentru implementarea acestei constructii se propune utilizarea primitivelor P si V. Fie semv, semaforul pe care se asteapta obtinerea accesului in regiunea critica si queue B, coada de asteptare in cazul in care nu este indeplinita conditia B. Secventa (prezentata in limbaj de descriere):
P(semv);
wait(B);
*secventa critica;
V(semv);
set(B)
nu satisface totdeauna cerintele din cauza ca este posibila situatia in care conditia B nu este indeplinita datorita activitatilor altor taskuri. Aceste taskuri, pentru a modifica valoarea expresiei logice B, trebuie la randul lor sa intre in regiuni critice controlate de aceeasi valoare comuna v. In acest caz taskurile se blocheaza reciproc.
Un exemplu de astfel de situatie este cel al doua sau mai multor taskuri care schimba intre ele mesajul prin intermediul unei cutii postale. Taskurile trebuie sa se excluda reciproc la depunere, sau preluare de mesaje. Fie semn semaforul care controleaza accesul la cutia postala (utilizand P(semv)). Un task nu poate prelua mesajul daca nu indeplineste conditia semhold.val > 0. Atunci el blocheaza accesul altui task, care vrea sa depuna un mesaj.
O implementare a constructiei de mai sus este secventa de forma:
repeat
P(semv);
if non B then
begin
V(semvv);
*secventa de relaxare;
endthen
until B;
reset B
*secventa critica;
set(B);
V(semv);
Mai sus s-a implementat o structura care executa o secventa de relaxare pentru verificarea accesului la variabila logica B, asupra careia s-au definit operatiile set si reset. Aceasta poate fi, de exemplu, o temporizare realizata prin intermediul ceasului.
7.3. Monitoare
In implementarea regiunilor critice conditionate apare dificultatea realizarii primitivei wait(B), C. A. R. Hoare si Brinch Hansen au propus un alt concept derivand din regiunile critice conditionate, numit monitor, si constand dintr-o structura de date impreuna cu procedurile de acces la ea. Taskurile pot citi si modifica datele, dar numai un singur task poate utilize procedurile (sau, o procedura a) unui monitor la un moment dat.
Folosirea de catre utilizator a unui monitor presupune secventa de forma:
type nume_monitor(parametri) = monitor;
begin
var
procedure entry nume_procedura 1(parametri);
var
begin..... end
procedure entry nume_procedura n(parametri)
var
begin......end
begin
*initializare date;
end
end
sistemul de operare sau mediul de executie construieste cozi de asteptare (vezi variabilele de tip eveniment). Fiecare dintre proceduri va fi cuprinsa intre primitivele wait si set astfel incat numai un task foloseste la un moment dat o procedura.
Pentru exemplificare, se construieste o cutie postala cu procedurile de acces la ea sub forma unui monitor. Se defineste tipul sequence of messages ca fiind o lista simplu inlantuita unde:
type message = record next: ^message;
content : array[1..N] of character
end
Se vor accepta asupra ei operatiile effectuate cu liste: init, append, etc.
type cutie_postala = monitor;
begin
var buf: sequence of messages;
notempty, notfull: event;
end
const max = ....;
procedure entry expediere (x : message);
begin
wait (notfull);
append (buf, x);
if k < max then set(notempty);
set
end
Daca implementarea schemei de mai sus s-ar face astfel incat numai un singur task sa intre la un moment dat in monitor, atunci in ipoteza ca trebuie sa astepte pentru a depune un mesaj intr-o cutie postala plina. Solutia de excludere mutuala de la utilizarea unei singure proceduri evita anomalia mentionata.
7.4 Expresii de cale
Forma generala a unei expresii de cale este:
path S end
unde S cuprinde o secventa de instructiuni, sau un numar oarecare de proceduri. Exista mai multe forme pentru descrierea expresiilor de cale:
path proc1; proc 2; proc3 end
path proc1;proc2;proc3 end
path n: (proc) end
path 1 (proc,proc2,proc3) end
Prima forma specifica faptul ca procedurile mentionate se vor executa strict in ordinea declarata. Daca un task solicita executia procedurii proc2, atunci el este pus in asteptare pana cand un alt task solicita executia procedurii proc1 si o termina de executat.
In cea de-a doua froma, procedurile pot fi utilizate in orice ordine.
In cea de-a treia forma, numarul n precedand caracterul ";" precizeaza catre taskuri pot apela simultan procedura proc n ar putea fi interpretata ca numarul de resurse pentru care concureaza procedura mentionata intre paranteze.
Cea de-a patra forma precizeaza ca numai un singur task poate executa la un moment dat una dintre procedurile declarate intre paranteze. Ordinea in care se executa nu mai este semnificativa.
De obicei notiunea de cale este asociata cu cea de obiect definit astfel:
type object = object path expresie de cale end
*declarare constante
*declarare tipuri de date
*declarare functii, proceduri, taskuri
*initializare
end
Se propune utilizarea acestui concept pentru implementarea rutinelor "send" si "receive" un tampon.
tampon = object
path max : 1 : (send); 1: (recieve)) end;
var buffer : array ..max-1] of message;
rp, wp : o..max-1;
end
entry procedure send(m : message);
begin
buffer (wp) := m;
wp := wp + 1 mod max;
end
init
begin rp := 0;
wp := 0;
end
end
7.5. Limbajul Modula-2
Limbajul de programare Modula-2 a fost introdus de Niklaus Wirth, extinzand limbajul PASCAL cu mecanismele de monitoare si de semnalizare. Dupa cum spune si numele limbajului, la baza lui sta conceptul de modul. Acesta este o colectie de proceduri si de declaratii globale, impachetate impreuna astfel incat numai numele declarate special pentru a fi accesibile din exterior pot fi utilizate in afara modulului. Procedurile care pot fi apelate din exteriorul modulului se numesc exporturi. Intr-un modul mai pot sa existe numele unor proceduri definite in alte module (declarate acolo de export) si care sunt definite de import.
Prin urmare, definitia unui modul este de forma:
DEFINITION MODULE nume;
END nume.
Implementarea unui modul este:
IMPLEMENTATION MODULE nume;
PROCEDURE p2.
BEGIN
END nume.
Un modul, spre deosebire de un proces, nu mai exista dupa ce programul a fost compilat. El este doar o structura sintactica in codul sursa prevazuta pentru a ajuta programatorul la controlul utilizarii identificatorilor.
Un program in Modula-2 poate fi implementat si executat ca un singur proces sau prin mai multe procese. Nu exista o structura sintactica speciala pentru a desemna un proces. Procesele pot fi create prin utilizarea unor proceduri din biblioteca standard. Un proces corespunde unui singur fir de executie. Doua procese pot executa acelasi cod. Executia proceselor poate sa sara de la o procedura la alta in acelasi modul sau in module diferite.
Limbajul Modula-2 admite utilizarea monitoarelor pentru implementarea excluderii mutuale. Este un modul in care doar un singur process poate sa intre pentru utilizarea unei proceduri la un moment dat.
Sintactic, un modul desemnat sa fie un monitor este definit in modul
urma:
DEFINITION MODULE nume[p];
IMPLEMENTATION MODULE[p];
unde p este prioritatea
monitorului.
Implementarea excluderii mutuale prin intermediul monitoarelor este simpla si convenabila, dar nu intotdeauna este eficienta.
O alta caracteristica a limbajului este reprezentata de conceptual de variabile de tip SIGNAL asupra carora pot fi folosite rutinele: WAIT(s), SENT(s) si INIT(s).
7.6. Limbaj ADA
Limbajul de programare Ada este utilizat pentru aplicatii de timp-real, sisteme inglobate si siteme distribuite.
Deoarece mecanismul de implementare a concurentei, reprezentat de monitor (in Modula-2), nu este convenabil pentru sisteme distribuite, acesta a fost inlocuit de un mecanism de transmitere de mesaje numit "rendezvous".
Acesta este un concept servind in special la comunicarea dintre taskuri, dar si la celelalte activitati legate de cooperarea lor. Comunicarea se face prin intermediul unei perechi de comenzi (instructiuni intr-un limbaj de nivel inalt).
Intr-un task se pot defini puncte de intrare prin intermediul unei directive entry. Aceste puncte sunt definite, de obicei, la inceputul taskului. Fiecarui punct de intrare ii corespunde cel putin o instructiune accept care poate sa cuprinda, la randul ei, o secventa de instructiuni(echivalenta unei proceduri) ce are ca variabile, fie parametri cuprinsi in numele punctului de intrare, fie alte variabile si constante ale taskului. Un alt task poate face apel la taskul precedent pentru a furniza, daca este cazul, date pentru a primi datele precizate in punctual de intrare.
In Ada procesele poarta denumirea de taskuri. Ele pot fi executate pe un sistem multiprocesor, pe un sistem distribuit sau pe un sistem monoprocesor prin intretesere. Exista o sintaxa speciala pentru definirea taskurilor.
In Ada declaratia accept este similara din punct de vedere sintactic cu o procedura normala cu exceptia faptului ca ea apare in interiorul codului unui task. Pozitia sa are importanta, spre deosebire de cazul unei proceduri normale.
Declaratia accept are sintaxa:
accept nume_intrare(argumente) do corp end nume_intrare;
unde nume_intrare este numele unui punct de intrare definit printr-o declaratie
entry nume_intrare(argumente)
La executia unei
declaratii accept se intampla
se copiaza valorile tuturor argumentelor de intrare de la taskul apelant la taskul care accepta;
taskul care accepta executa secventa de instructiuni definite de entry.
se copiaza valorile argumentelor de iesire de la taskul care accepta la taskul apelant;
cele doua procese sunt executate mai departe independent unul de altul in mod concurent.
Exemplu:
task 1 is:
type T1=...
type T2=..
-defineste punctual de intrare;
entry nume1(x in : T1 y out : T2);
entry nume2(parametri);
entry nume3
accept nume1 (x in : T1 y out : T2) do
*secventa de instructiuni 1;
end nume1;
...........
accept nume2(parametri) do
*secventa de instructiuni 2;
end nume2;
...........
accept nume3(parametri) do
*secventa de instructiuni 3;
end nume3;
...........
accept nume3 do
.............
task 2 is:
var x2 : T1;
y2 : T2
end
task1.nume(x2, y2);
task1.nume(parametri);
task1.nume2(x2, y2);
--
.............
task1.nume3;
.............
Pentru fiecare
punct de intrare se creeaza o coada de asteptare.
Daca task1 ajunge mai repede sa execute accept nume1, atunci el
asteapta ca task2 sa ajunga sa execute intructiunea task1.nume1(.). In continuare, se
executa secventa respectiva din task1. Dupa aceasta,
taskurile isi continua activitatea
Daca task2 ajunge sa execute mai repede instructiunea task1.nume1, atunci el asteapta ca task1 sa ajunga la executia instructiunii accept nume1.
In fig.1 este reprezentata simbolic executia celor doua taskuri.
task 1 is entry nume1(parametri) entry nume2(parametri) entry nume3 accept nume1(parametri) do parametri secventa instructiuni 1 end nume1 accept nume2(parametri) do parametri secventa instructiuni 2 end nume2 accept nume3(parametri) do secventa instructiuni 3 end nume3 accept nume3 do |
Date de la task2 la task1.. -----Date de la task1 la task2 -------------date----- ----- ------ |
task 2 is task1.nume1(parametri) task1.nume2(parametric) task1.nume2(parametric) task1.nume3 |
Fig. 7.1 Reprezentarea executiei concurente a taskurilor prin rendezvous
Dupa cum se vede din aceasta figura, comunicatia poate fi bilaterala, adica in apel pot sa intre atat variabile de intrare cat si de iesire. Exista un task apelant (task2) si unul care accepta apelul (task1) . Comunicatia dintre cele doua este asimetrica , deoarece numai taskul apelant precizeaza in comanda sa taskul apelat . Cel din urma , insa , poate fi apelat de orice task apelant . Daca ar fi simetrie , atunci fiecare task si-ar preciza patenerul .
Se constata ca :
Daca task1 contine el insusi o instructiune task1.nume1 , atunci el se
blocheaza .
Punctul de intrare nume3 serveste doar pentru sincronizare .
Pot sa existe apeluri la un punct de intrare dintr-un anumit task in mai multe taskuri .
In secventa de instructiuni cuprins intr-o instructiune accept , poate sa fie cuprinsa o alta instructiune accept . Astfel se pot realiza sincronizari intre mai multe taskuri .
Conceptul de rendezvous poate fi implementat si prin apelul functiilor dintr-un nucleu al unui sistem de operare multitasking . La declararea unui punct de intrare (entry) intr-un task , i se asociaza acestuia doua semafoare :
var sem1,sem2 : semaphore;
si se initializeaza :
sem1.val:=0;
sem2.val:=0;
La executia instructiunii accept nume(.) se impacheteaza secventa de instructiuni corespunzatoare astfel :
P(sem1);
* preia parametri depusi de taskul cu care coopereaza ;
* executa secventa de instructiuni ;
V(sem2);
P(sem1);
Taskul care face apel la punctul de intrare va executa secventa :
* depune parametrii ( daca este cazul );
V(sem1);
P(sem2);
* preia parametrii din task1 ;
V(sem1);
O alta caracteristica a limbajului Ada o constituie asteptarea selectiva .
Aceasta are mai multe variante de secvente ce pot fi selectate pentru a realiza un rendezvous .Un exemplu de utilizare intr-un task este :
............
select
accept nume1(parametri) do
* secventa de instructiuni 1
end nume1
or
accept nume2(parametri) do
* secventa de instructiuni 2
end nume2
or
accept nume3(parametri) do
* secventa de instructiuni 3
end nume3
end select
Se alege , in mod aleator , alternativa care duce la realizarea rendezvous-ului . Daca nici o alternativa nu este realizabila ( nu se solicita imediat nici un rendezvous), atunci se asteapta ca una din alternative sa fie "deschisa".
Taskul ce cuprinde secventa precedenta asteapta pana cand un alt task apeleaza punctele de intrare nume1 , nume2 sau nume3 .
O extensie a instructiunii select o constituie introducerea unui timp de asteptare limitat. Daca intr-un interval de timp precizat , nici un task nu solicita un rendezvous,
adica nu apeleaza nici unul dintre punctele de intrare cuprinse in select , atunci taskul isi continua activitatea .Taskul va cuprinde secventa :
............
select
accept nume1(parametri) do
* secventa de instructiuni 1
end nume1
or
accept nume2(parametri) do
* secventa de instructiuni 2
end nume2
or
wait(time);
end select
............
In secventa de mai sus , instructiunea wait(time) are efectul de asteptare a unui interval de timp (time) .
Un alt tip de instructiune select este cea care implementeaza conditii pentru realizarea unui rendezvous pe un anumit punct de intrare , adica pentru realizarea unui accept . Se da ca exemplu un task ce serveste ca element de intercomunicare si care controleaza accesul altor taskuri la o cutie postala numita tampon .
task intercomunicare is :
var tampon:pob;
...........
entry send(x in : message);
entry receive(x out : message);
.........
begin
* initializare tampon;
repeat
select
when semfree.val>0;
accept send(x in : message);
* secventa de instructiuni (procedura send);
end send
or
when semhold.val>0;
accept receive(x out : message);
* secventa instructiuni (procedura receive);
end receive
end select
Conceptul de rendezvous poate fi intalnit si in alte limbaje cum ar fi CSP (Communicating Sequential Pocesses dar sub alte forme .
7.7 Limbajul Java
Limbajul de programare Java este complet orientat pe obiecte si este neutru din punct de vedere arhitectural . Java este un limbaj compilat si interpretat . Aceasta inseamna ca dupa editarea unui program , codul sursa este compilat intr-un format de nivel mediu ,numit cod de octeti . Acesta este destinat unei masini virtuale Java. Pentru executia lui pe un calculator real , trebuie sa fie interpretat mai intai de un mediu de executie Java . Aplicatiile Java pot fi executate pe sisteme monoprocesor , sisteme multiprocesor sau sisteme distribuite .
In aceasta sectiune sunt prezentate doar conceptele din Java care privesc implementarea aplicatiilor de timp-real . Pentru insusirea limbajului se recomanda consultarea bibliografiei .
Din punct de vedere al limbajului Java un proces este un program care este executat in propriul sau spatiu de adresa . Java este un sistem cu multiprelucrare , prin urmare , accepta mai multe procese care sunt rulate concurent in propriile lor spatii de adresa .Un fir este o secventa de executie din cadrul unui proces . Deci un fir poate fi executat numai in contextul unui proces .
Crearea si executia firelor
Un fir trebuie declarat , creat , startat , oprit si eventual distrus .
Crearea claselor care folosesc fir de executie se face prin extinderea clasei Thread sau prin implementarea clasei Runnable .
Crearea firelor prin extinderea clasei Thread se face prin declaratii de forma urmatoare :
public class NumeFir extends Thread
}
S-a definit un nou fir numit NumeFir care executa ceva la apelul metodei run() . Metoda run() este asemenatoare ca functionalitate cu functia main() din limbajul C sau C++ . Ea contine corpul principal al codului care va fi rulat de firul de executie.
Implementarea interfetei Runnable face ca clasele existente sa fie convertite in fire de executie fara modificarea claselor de baza .Formatul utilizat in acest caz este :
public class NumeFir extends ImportClass
implements Runnable
}
Implementarea interfetei Runnable are ca efect realizarea unui fir de executie separat. Firele trebuie instantiate in ambele cazuri .
Pentru crearea unei instante a clasei NumeFir trebuie adaugata declaratia :
NumeFir nume = new NumeFir();
Firele create trebuie lansate in executie . Aceasta se realizeaza pentru exemplul dat cu :
nume.start();
Executia unui fir poate fi suspendata temporar cu metoda stop(), adica pentru exemplul dat :
nume.stop();
Executia unui fir poate fi suspendata temporar cu metoda suspend() .
Pentru exemplul dat aceasta este :
nume.suspend();
sau poate fi reluata cu metoda resume() , adica :
nume.resume();
Metoda stop() nu distruge firele . Pentru a elimina complet un fir trebuie apelata metoda destroy(). In Java nu este obligatorie distrugerea firelor. Sistemul Java de colectare a gunoiului rezolva aceasta problema . Pentru a avea certitudinea ca au fost sterse toate referintele la obiectul fir de executie de firul nume , se recomanda utilizarea instructiunii :
nume = null;
Operatia forteaza sistemul pentru colectarea gunoiului sa programeze dezalocarea resursei pentru obiectul nume .
Un exemplu de program care are pe langa firul principal de executie inca doua (Thread-1 si Thread-2) este urmatorul :
class Testfire extends NumeFir // asteapta terminarea primului fir
catch (InterruptedException ignored)
try // asteapta al doilea fir
catch (InterruptedException ignored)
nume1.stop(); // opreste executia primului fir
nume2.stop(); // opreste executia celui de al doilea fir
nume1 = null; // elibereaza legaturile primului fir
nume2 = null; // elibereaza legaturile pentru al doilea fir
System.out.println( Terminat executie fire
}
}
public class NumeFir extends Thread
Metoda getName() a fost utilizata cu scopul de a obtine numele firului care o
solicita .
Programul de mai sus va scrie pe ecran :
***Aplicatie cu fire***
Firele au fost startate
Acesta este firul : Thread-1
Acesta este firul : Thread-2
Terminat executie fire
Application Exit .
Instructiunea try este folosita cu scopul de a informa interpretorul Java ca un bloc de date poate genera o exceptie si ca tratarea ei se va face in instructiunea imediat urmatoare . Instructiunea sau blocul (grupul) de instructiuni ce urmeaza dupa try corespunde codului care poate genera o exceptie . Dupa instructiune sau blocul respectiv urmeaza codul care trateaza exceptia .
Ca exemplu de utilizare a interfetei Runnable se prezinta o aplicatie in care functia main creeaza 3 fire de tip a si 3 fire de tip b , care se intretes doua cate doua .
public class TestRunnable // asteapta terminarea firului creat
catch (InterruptedException ignored)
ta.resume(); // reactiveaza firul de tip a
try // asteapta terminarea firului creat
catch (InterruptedException ignored)
}
}
public class InterfataRunnable_a inplements Runnable
public class InterfataRunnable_b inplements Runnable
Programul va scrie pe ecran :
Noul fir creat de: main
Fir de tip a cu indicatorul: Thread-1
Fir de tip b cu indicatorul: Thread-2
Firul Thread-1 isi incheie activitatea
Noul fir creat de: main
Fir de tip a cu indicatorul: Thread-3
Fir de tip b cu indicatorul: Thread-4
Firul Thread-3 isi incheie activitatea
...............
Sincronizarea firelor
Un obiect sau o metoda pot fi accesate de mai multe fire de executie . Pentru a realiza excluderea mutuala de la executarea unei sectiuni critice se poate folosi cuvantul cheie synchronized().Ca exemplu de sincronizare este :
public void numeIndex()
}
Metoda numeIndex contine un bloc sincronizat . Obiectul index este blocat de un fir care a inceput utilizarea metodei pana la terminarea executiei blocului respectiv .
Tratarea exceptiilor
Dupa cum s-a vazut si din exemplele anterioare , limbajul Java ca de altfel si limbajul Ada ofera posibilitati de tratare a exceptiilor . Exceptiile sunt erori remediabile . Tratarea exceptiilor face programele mai robuste si mai performante. Aceasta este o cerinta foarte importanta pentru aplicatiile de timp-real .
In Java , exceptiile sunt considerate o clasa de obiecte care pot trata diferite probleme aparute in executie .
Exista trei moduri pentru tratare a exceptiilor :
ignorarea lor ;
prelucrarea lor de catre codul in care apar ;
semnalizarea lor codului care a apelat metoda ce a generat exceptia pentru a fi prelucrata de catre aceasta .
Exceptiile care nu sunt tratate explicit de catre program sunt transmise interpretorului Java care le trateaza sau opreste executia programului .
Exista mai multe metode oferite de limbajul Java pentru tratarea exceptiilor :
catch care dupa cum spune si numele ei (interceptare) cuprinde codul care
detecteaza aparitia unei exceptii si o trateaza in mod corespunzator.
try specifica faptul ca programul va incerca sa execute un bloc de cod care
genera o exceptie .
finally precizeaza actiunile care urmeaza a fi executate in cazul in care nici una dintre instructiunile catch anterioare nu a rezolvat problema .
Planificarea firelor
Exista mai multe moduri de executie a firelor unui program scris in Java . Unul este prin divizarea timpului ( engl. : timeslicing ) si consta din alocarea periodica a unor mici cuante de timp fiecarui fir .Celalalt mod implica planificarea firelor . Variante mai vechi ofereau numai planificarea nepreemtiva . Tendinta de evolutie a mediilor de excutie Java este de includere a facilitatilor necesare unor aplicatii de timp-real . Acestea ar trebui , in primul rand , sa poata specifica explicit obiecte rezidente permanent in memoria interna ( operativa ) a sistemului si sa aplice planificarea preemptiva .
7.8 Cooperarea si concurenta
Cand intr-un calculator exista mai multe procese care trebuie si pot fi executate in acelasi timp , se spune ca ele sunt executate concurent . Dupa cum s-a mentionat , daca sistemul de calcul este construit cu un singur procesor , executia concurenta a proceselor se realizeaza prin intretesere . In cazul unui sistem de calcul multiprocesor, problema intreteserii se pune numai atunci cand numarul proceselor care trebuie executate la un moment dat depaseste pe cel al procesoarelor . Sistemele multiprocesor ofera posibilitatea prelucrarii paralele , in timp ce sistmele monoprocesor o ofera numai pe cea a unei prelucrari virtual paralele .
Indeplinirea activitatilor pe care trebuie sa le realizeze ansamblul de taskuri pentru implementarea algoritmilor de control, implica cooperarea lor .
Aceasta consta din :
sincronizarea dintre taskuri ;
sincronizarea cu timpul-real ;
excluderea mutuala de la utilizarea resurselor ;
comunicarea dintre taskuri.
Este dificil de realizat o diferentiere completa a acestor activitati. De exemplu, efectuand o operatie de comunicare prin rendezvous, doua taskuri realizeaza implicit o sincronizare intre ele.
Sincronizarea cu timpul-real
In capitolul s-au prezentat rutinele tdelay, twake si wait. Cea de-a doua rutina serveste la reactivarea unui task, la un moment precizat. Pentru executia periodica (cu perioada deltat) a unei activitati se presupune secventa:
repeat
today(deltat : tine);
*secventa de instructiuni a activitatii:
wait;
forever;
Trebuie remarcata diferenta fata de secventa:
repeat
today(deltat : tine);
wait;
*secventa de instructiuni a activitatii:
forever;
In primul caz, activitatea va incepe intotdeauna virtual, cu perioada deltat. In cel de-al doilea caz este fixa doar durata asteptarii, iar perioada de repetitie a activitetii nu mai este constanta. Intervalul de timp cu care se repeta activitatea este mai mare decat deltat, din cauza timpului necesar pentru executia secventei. Trebuie mentionat ca durata de executie a secventei de instructiuni poate fi variabila.
Un alt mod de realizare a temporizarilor se bazeaza pe cutiile postale, folosind rutinele:
send (cutie: :pob : mes mesage
si
receive ( cutie : pob: var mes ^mesage: deltat : time);
Intarzierea executiei unei secvente de instructiuni cu un interval de timp "deltat" se poate realiza cu secventa:
repeat
receive (cutie, mes, deltat);
*secventa de instructiuni
forever
Executia periodica a unei secvente de instructiuni aplica doar doua taskuri ca in exemplul urmator:
*secventa de initializare :
var cutie1, cutie2, cutie3 : pob;
deltat : time;
end;
send( cutie3, mes);
.....
task 1 is;
var mes : message;
.....
repeat
recive cutie1,mes,deltat
recive(cutie3,mes);
send(cutie3);
forever
task 2 is;
.
repeat
recive(cutie2,mes);
send(curie3,mes);
*secventa de instructiuni;
forever
Task 1 are numai un rol de planificator.Task 2 se repeta cu perioada fixata de task 1. Pentru fiecare task ce necesita asfel de intarzieri trebuie
construit cate un asfel de planificator.
O alta posibilitate de sincronizare cu timpul-real este oferita de conceptul
de rendezvous ( din Ada ) prin directiva select, cu optiunea wait ( time ).
Sincroarizarea proceselor intre ele
Sincronizarea consta din orice constrangere impusa ordonarii operatiilor in timp, intre doua sau mai multe taskuri. Sincotizarea poate fi explicita, atunci cand se realizeaza folosind rutine special dedicate acestui scop, sau implicita, atunci cand se obtine ca un rezultat (auxiliar) al utizarii unor rutine destinate altui scop. De exemplu utilizand metodele suspend si resume, sau synchronized ( din Java), se obtine o sincronizare explicita, dar utilizand send si recive, se obtine o sincronizare ca un rezultat auxiliar al comunicarii.
Sincronizarea dintre taskuri poate fi reciproca sau unilaterala. Astfel
taskurile:
task 1 is;
begin
repeat
*evenimentul A;
*continua daca a avut loc evenimentul B;
.
forever
end task 1;
task 2 is;
begin
repeat
*evenimentul B;
*continua daca a avut loc evenimentul A;
..
forever
end task 2;
se sincronizeaza reciproc. Fiecare trebuie sa-l astepte pe celalalt, daca acesta nu a ajuns in punctul de sincronizare.
Daca in task 1 ar lipsi *continua daca a avut loc evenimentul B; atunci
s-ar obtine o sincronizare unilaterala. In acest caz, task 2 asteapta ca task 1 sa efectueze "evenimentul A", si chiar daca nu a avut loc evenimentul B, task 1 isi
va continua activitatea.
1. Sincronizarea cu rutinele delay si continue
Exemplul 1:
Sincronizarea reciproca:
*secvente de initalizare:
var queue1, queue2 : ^tcb;
begin
init (queue1);
init (queue2);
..
task 1 is;
begin
repeat
.
continue (queue2);
delay (queue1);
.
forever end task 1;
task 2 is;
begin
repeat
.
continue (queue1);
delay (queue2);
forever
end task 2;
Secventa de initializare este realizeata de un task oarecare (sau chiar de
unul din cele doua taskuri care intra mai intai in executie).
Sincronizarea cu primitivele P si V
Exemplul 2:
Sincronizare reciproca
*secventa de initializare:
var sen1, sem2 : semaphore;
begin
init (sem1.next);
init (sem2.next);
sem1.val : =0;
sem2.val : =0;
task 1 is; task 2 is;
begin begin
repeat repeat
.
V(sem2); V(sem1);
P(sem1); P(sem2);
forever forever
end task1; end task2;
Evenimentul de activare este asteptat prin operatia P si se semnaleaza
prin operatia V.
Daca exista certitudinea ca unul dintre taskuri "soseste" mai repede in
punctul de sincronizare, atunci aceasta ( sincronizarea ) se poate realiza cu un
singur task.
Un semafor sp este propriu sau privat pentru un anumit task, daca numai el poate executa operatia P ( sp ). Celelalte taskuri pot executa numai operatia V
( sp ).
Exemplul 3:
Sincronizarea a n taskuri
*secventa de initializare:
var sp,sm,se : semaphore;
b1,b2,,bn : boolean;
end
begin
b1 :=true;
b2 :=false;
.
bn := false;
init(sp.next);
init(sm.next);
init(se.next);
sm.val :=1
sp.val :=0
se.val :=0
..
task1 is;
.
P(sm);
If b1 and b2 and and bn thenV (sp);
else b1 :=false;
V(sm);
P(sp);
for 1 :=2 to n do V (sp);
.
task i is;
.
P (sm);
b1 :=true
if b1 and and bn then V(sp);
V(sm);
P(se);
Secventele cuprinse intre P (sm) sunt sectiuni critice, si se executa prin excludere mutuala.
Sincronizarea cu variabile de tip eveniment
Exemplul 4:
Sincronizare recipoca
*segventa de initializare:
Var ev1,ev2 : event;
begin
init(ev1.next);
init(ev2.next);
reset(ev1);
reset(ev2);
task 1 is; task 2 is;
begin begin
.
set(ev2); set(ev1);
wait(ev1) ; wait(ev2);
.
end task 1 end task 2
Sincronizarea cu rutinele send si recive
Se pot utiliza pentru sincronizarea doua cutii postale care sunt initial goale. Un task va astepta incercand sa citeasca un mesaj dintr-o cutie postala goala si-l va scoate pe altul din asteptare, depunand un mesaj in cutia lui postala.
5 Sincronizarea prin regiuni critice conditionate
Exemplul 5:
Sincronizare reciproca
*secventa de initializare:
var v:shared T;
b,b:boolean;
begin
b:=false;
b:=true;
task 1 is; task 2 is;
.. .
b:=true b:=true
with v then b do Q; with v then b do Q;
b:=false; b:=false;
..
In exemplu din figura 7.1 s-a realizat si o operatie de sincronizare in punctul de intrare "nume3".
Un alt mod de a realiza operatia de sincronizare prevede utilizarea in acest scop a continuturilor procedurilor (a se vedea utilizarea cutii postale).
Sincronizarea prin expresii de cale
Exista mai multe variante pentru realizarea operatiei de sincronizare.
Exemplul 6
Fie declaratia:
Path proc 1; proc 2;proc 3 end;
si urmatoarele doua task-uri:
task1 is; task 2 is;
..
proc 1; proc 2;
proc 3; .
..
Este evidenta sincronizarea celor doua task-uri. In mod similar se poate realiza sincronizarea mai multor task-uri.
In exemplul precedent, continuturile procedurilor nu au importanta pentru sincronizare.
Un alt mod de realizare a sincronizari este acel care utilizarea conceputul de obiect si se specifica utilizarea lui specifica.
Excluderea mutuala de la utilizarea resurselor
O resursa se numeste proprie, daca ea este utilizata numai de un singur task pe tot parcursul activitatii ansamblului de taskuri. Ea se numeste comuna sau partajabila, daca este urilizata de cel putin doua taskuri. O resursa se numeste partajabila cu n puncte de acces (n>1), daca poate fi utilazata simultan de n taskuri. Ea se numeste resursa critica, daca este comuna si are un singur punct de acces, adica, la un moment dat poate fi utilizata numai de un singur task desi exista mai multe solicitari.
Mai multe taskuri care utilizeaza simultan, in comun, o resursa cu mai multe puncte de acces, se numesc paralele din punctul de vedere al resursei. Daca mai multe taskuri utilizeaza o resursa critica, atunci ele trebuie sa se excluda mutua l(sau reciproc, in timp) de la folosirea ei. Secventa de instructini dintr-un task in care se utilizeaza resursa critica se numeste sectiune critica.
O sectiune critica trebuie sa fie cat mai scurta posibil, astfel incat sa nu intarzie prea mult celelalte taskuri(unele avand poate o prioritate mai mare). Prima varianta de excludere reciproca a fost realizata hard, prin generarea unui semnal cand un task intra intr-o sectiune critica. Acest smnal inhiba sistemul de intreruperi, astfel fiind impiedicate alte taskuri sa intre in executia. Dezavantajul acestei metode consta din dificultate realizari unor inhibari selective si dinamice.
Ecluderea mutuala cu rutinele delay si continue
Trebuie sa se construiasca un indicator de acces la secventa critica. Acesta va lua valoarea false inainte de a intra in sectiune critica si true la iesire. Daca atunci cand vrea sa intre in sectiunea critica indicatorul are valoarea false atunci asteapta pe coade queue.
Exemplul 7 :
var ias: boolean;
queue: ^tcb;
end
*secventa de initializare:
ias:=true;
init (queue);
Un task care trebuie sa intre in sectiunea critica controlata de ias va executa secventa:
if not ias then delay (queue);
ias:= false;
*utilizare resursa;
ias:=true;
continue (queue);
Inconvenientul acestei metode consta in faptul ca, daca un task a asteptat sa obtina accesul la sectiunea sa critica si a fost reactivat, se poate intampla ca un alt task cu prioritate mai mare ca a sa, sa ocupe resursa. Prin urmare, se poate ajunge la situatia in care resursa sa fie utilizata in comun.
Anomalia nu apare daca exista siguranta ca accesul la variabila ias este invizibila, mai precis ca primele doua linii ale secventei precedente sunt indivizibile.
Excluderea mutuala cu primitivele P si V
Primitivele P si V ofera posibilitatea unei metode simple si sigure pentru excluderea mutuala. Aceasta consta din construirea unui semafor pentru fiecare resursa cu un singur punct de acces.
Exemplul 8:
*secventa de initializare:
var: semresursa : semaphore;
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 2597
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved