Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateC
C sharpCalculatoareCorel drawDot netExcelFox pro
FrontpageHardwareHtmlInternetJavaLinux
MatlabMs dosPascalPhpPower pointRetele calculatoare
SqlTutorialsWebdesignWindowsWordXml

Programare in timp real - Limbaje timp -real si notiuni de programare concurenta

calculatoare



+ Font mai mare | - Font mai mic



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;

  • viteza relativa cu care este executat un proces;
  • in care punct este suspendat un proces pentru executia prealabila a altuia;
  • alte temporizari ale proceselor.

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:

  • multiprogramare;
  • multiprelucrare;
  • prelucrare distribuita.

Cooperarea proceselor consta din:

  • comunicare ~ schimbul de date de la un proces la altul;
  • sincronizare - ordonarea secventelor in unele puncte din procese conform specificatiilor.

Cele mai importante probleme ridicate de executia concurenta a programelor sunt:

1. Executia concurenta pe un sistem monoprocesor care implica:

  • comutarea proceselor- deci existenta unui mecanism de comutare.
  • Planificarea -adica sa existe un algoritm care sa decida cand sa comute de la u proces la altul si care sp fie urmatorul proces de executat.

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:

  • Articolele de date sa fie receptionate de catre consumator in aceeasi ordine in care au fost transmise de catre producator.
  • Sa nu piarda nici un articol de date prin transferul de la producator la consumator.
  • Sa nu fie inserate alte articole (sau duplicate ale unora existente) in timpul transferului.

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 :

  • Orice numar de procese cititoare sa poata accesa simultan (fara excludere mutuala) fisierul de date;
  • Daca un proces scriitor a inceput sa depuna date in fisier, nici unui task cititor nu trebuie sa i se permita accesul in fisier.

5. Problema cititori si scriitori poate fi o generalizare a problemei precedente prin existenta mai multor taskuri scriitoare. Cerintele de sincronizare contsau din :

  • Orice numar de procese cititoare sa poata accesa simultan fisierul de date;
  • Numai un singur proces scriitor sa poata accesa la un moment dat fisierul.
  • Daca un proces scriitor acceseaza fiierul, nici un proces cititor sa nu-l acceseze in aceea perioada de timp.

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:

  • Mecanisme hardware.
  • Mecanisme software.

La randul lor mecanismele software pot fi realizate in :

  • Nucleul sistemului de operare.
  • Mediul de executie a programului.
  • Biblioteci realizate de catre programatorul aplicatiei

Cele mai cunoscute mecanisme hardware pentru implementarea concurentei sunt :

  • Instructiuni pentru blocarea si deblocarea sistemului de intreuperi.
  • Instructiuni pentru rezervarea si renuntarea la accesul exclusiv la zone de memorie.
  • Instructiuni individuale executabile din procese diferite pentru incrementarea sau decrementarea unei variabile comune.
  • Transmiterea mesajelor in sistemele distribuite.

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:

  • sa fie usor de integrat cu metodele de proiectare utilizate in mod curent;
  • sa fie flexibile;
  • programele sa fie portabile;
  • programele sa fie usor de inteles pentru eventuale reactualizari sau corectari;
  • sa fie eficiente din punctul de vedere al vitezei de executie st al cerintelor de   memorie;
  • sa garanteze securitatea operatiilor in timpul compilarii;
  • sa garanteze securitatea in timpul executiei programelor;
  • sa permita utilizarea unor metode de proiectare si compilare modulare;
  • sa permita abstractizarea datelor;
  • sa fie eficiente din punctul de vedere al structurilor de control al fluxurilor de date;
  • sa ofere facilitati pentru utilizarea resurselor la nivelul masinii;
  • sa ofere facilitati pentru tratarea exceptiilor;
  • sa permita si sa fie eficiente in tratarea intreruperilor;
  • sa permita interfatarea cu proceduri scrise in limbaje de asamblare;
  • sa aiba posibilitatea de interfatare cu alte limbaje de nivel inalt;
  • sa fie multitasking - cea mai importanta cerinta.

Printre cele mai cunoscute limbaje utilizate pentru aplicatiile de timp-real sunt:

  • CORAL66 - acronimul din engleza de la Computer On-line Real-time Application Language (aparut in anul 1966);
  • Pascal (in varianta concurenta) - aparut in anul 1970
  • RTL/2 - acronimul de la Real Time Language Two (dezvoltat in anul 1971);
  • C- proiectat in 1972. (Posix)
  • Forth - dezvoltai prin anii 1970;
  • PL/M - acronimul de la Programming Language/Microcomputers dezvoltat in 1976;
  • Ada - dezvoltat in 1970
  • Modula-2 - dezvoltat intre anii 1977 si 1980;
  • Java - aparut in anii  

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 urmatoarele:

  1. Daca un task realizeaza un apel entry inainte ca un alt task sa execute o declaratie accept cu acelasi nume, taskul apelant este suspendat.
  2. Daca un task executa o declaratie accept inainte ca un alt task sa realizeze un apel entry cu acelasi nume(definit de accept), primul task este suspendat.
  3. Numai daca, atat taskul apelant al declaratiei entry, cat si taskul care accepta (executa accept) un apel cu acelasi nume au executat instructiunile corespunzatoare, are loc un rendezvous. In acest caz se executa secventa de instructiuni definite de entry:

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.

  1. Un alt rendezvous poate avea loc numai daca taskul care a acceptat un rendezvous a executat toata secventa implicata de acesta.
  2. Daca mai multe taskuri apeleaza acelasi punct de intrare entry, ele sunt puse intr-o coada de asteptare si sunt servite in ordinea apelurilor.

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 independent (pana, eventual la un nou rendezvous).

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;

..

  1. Sincronizarea prin rendezvous

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



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 2603
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved