Sunteți pe pagina 1din 67

7

Cercul de programare .NET ADF


5/30/2013 C4 - .NET ADF (c) Victor Adrian Prisacariu

In momentul in care este lansat in executie un program sistemul de operare creaza un proces. Procesul este, deci, o instanta a unui program in executie Sistemele de operare multitasking pot rula simultan mai multe procese. Un proces, in general, are urmatoarele componente:
O imagine a codului executabil asociat unui program Memoria asociata procesului, ce contine cod executabil asociat procesului si alte date specifice acestuia. Descriptori de resurse ai sistemului de operare. ex: descriptori de fisier (terminologie Unix) sau handles (terminologie Windows). Atribute de securitate, ca de ex posesorul (owner) procesului sau setul de permisii al procesului. Starea procesorului (contextul) format din datele din registrii etc. Starea procesului este in general stocata in registrii cat procesul se executa si in memoria externa procesorului cand procesul nu se executa.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

Un thread (fir de executie) este o modalitate de a imparti un proces in mai multe bucati ce ruleaza simultan (bucati numite task-uri). Thread-uri multiple pot fi executate in paralel pe acelasi calculator sau pe calculatoare diferite. Multithreading-ul se poate face in 2 feluri:
folosind Time slice-uri. Procesorul executa un thread o perioada de timp si apoi trece la executia altui thread. Aici apare iluzia unui multitasking. pe sistemele multiprocesor. Procesorul are mai multe core-uri sau sistemul este multiprocesor.

Sistemele de operare noi pot implementa ambele tipuri de multithreading simultan prin intermediul unui scheduler.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

Scheduler-ul poate avea doua modalitati de a alege ce thread se executa la un moment dat:
preemptive: Sistemul de operare determina ce schimbare de context (ce thread) trebuie sa aleaga in continuare. cooperative: Thread-urile renunta singure la executie

Thread-urile unui proces impart spatiul de memorie. In unele situatii apare o distinctie intre threadurile kernel-level si user-level. Primele sunt organizate si programate pentru rulare de catre kernel. Urmatoarele sunt organizate de catre user. Programarea threadurile user-level in interiorul procesului este facuta de catre utilizator. Kernelul sistemului de operare permite manipularea threadurilor prin apel de functii sistem.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

Concluzie:
Un proces este cea mai mare unitate a unui scheduler multitasking. Procesele detin resurse sistem (memorie, file handle-uri, device handle-uri, ferestre windows, etc.). Procesele NU impart spatiul de memorie (si handle-urile) cu alte procese decat prin apeluri explicite de functii. Procesele in general sunt programate pentru executie preemptiv. Un thread este ce mai mica unitate a unui scheduler multitasking. Cel putin un thread exista intr-un proces. Daca exista mai multe threaduri in proces acestea impart spatiul de memorie si handle-urile. Thread-urile sunt executate preemptiv. Thread-urile nu detin resurse cu exceptia unei stive si a unei copii a registrilor.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

In C# vom folosi clasa Thread pentru lucurul cu fire de executie. Proprietati:


IsAlive: precizeaza daca threadul este in executie sau nu IsBackground: precizeaza daca threadul este rulat in background sau nu IsThreadPoolThread: precizeaza daca threadul este in thread pool Name: numele thread-ului Priority: prioritate ManagedThreadId: Un id unic alocat de sistem pentru thread diferit de id-ul dat de OS ThreadState: Aborted, AbordRequested, Background, Running, Stopped, StopRequested, Suspended, SuspendedRequested, Unstarted, WaitSleepJoin
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

Metode:
Abort: Arunca o exceptie thread-ului (ThreadAbordException) pentru a-i indica ca trebuie sa se opreasca Interrupt: Arunca o exceptie thread-ului (ThreadInterruptException) cand acesta este in o stare de asteptare (WaitSleepJoin). Join: Blocheaza thread-ul apelant pana cand thread-ul care are metoda join se termina. Resume/Suspend: a nu se folosi; sunt obsolete Start: porneste thread-ul
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

Metode si proprietati statice:


Proprietati:
CurrentContext: intoarce un obiect de tip ThreadContext asociat thread-ului ce ruleaza acum CurrentPrincipal: user-ul detinator al thread-ului ce ruleaza acum CurrentThread: tread-ul ce ruleaza acum

Metode:
BeginCriticalRegion, EndCriticalRegion, Sleep, ResetAbort, etc.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

10

1> Cream o metoda statica fara argumente si care nu intoarce nici o valoare. 2> Cream un delegat ThreadStart si il legam la metoda de mai sus. 3> Cream un obiect Thread ce primeste ca parametru obiectul ThreadStart 4> Apelam metoda Thread.Start
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

11

static void SimpleWork() { Console.WriteLine(Thread {0}, Thread.CurrentThread.ManagedThreadId; } ThreadStart operatie = new ThreadStart(SimpleWork);

Thread threadul = new Thread(operatie);


threadul.Start();
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

12

threadul.Join(); Apelarea metodei Join opreste aplicatia noasta sa astepte oprirea thread-ului. Ex:
Thread[] threaduri = new Thread[5]; for (int i=0;i<5;i++) { threaduri[i] = new Thread(operatie); thread[i].Start(); } for (i=0;i<5;i++) threaduri[i].Join();

Acest exemplu creaza 5 threaduri si apoi asteapta terminarea fiecaruia.


5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

13

Putem influenta modul in care schedulerul programeaza thread-urile modificand prioritatea acestora. Pentru a modifica prioritatea unui thread setam proprietatea Priority cu una din valorile din enumerarea ThreadPriority:
HighPriority AboveNormal Normal BelowNormal Lowest

In general nu veti creste performanta unui thread prin cresterea prioritatii acestuia.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

14

In exemplele de mai sus am trimis la Thread un delegat ce pointa la o metoda fara nici un parametru. Daca dorim sa apelam o metoda cu parametru vom folosi delegatul ParameterizedThreadStart. Noua metoda va avea un parametru un obiect de tip object. Noul delegat va fi trimis ca parametru la crearea thread-ului (exact ca la ThreadStart simplu).

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

15

static void CuParametru(object obj) { string info = (string) obj; Console.WriteLine(obj + Thread Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); } ParametrizedThreadStart operatie = new ParametrizedThreadStart(CuParametru); Thread threadul = new Thread(operatie); threadul.Start(Salutari);
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

16

Mecanismul principal de oprire a unui thread este apelarea metodei Abort(); Cand este apelata aceasta metoda, sistemul de threading arunca in thread o exceptie de tip ThreadAbortException. Indiferent daca exceptia este prinsa sau nu thread-ul este oprit.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

17

static void AcestThread() { OClasa.IsValid=true; OClasa.IsComplete=true; OClasa.WriteToConsole(); } Thread threadul = new Thread(new ThreadStart(AcestThread)); In momentul in care trimite abort la acest thread, din cauza ca nu este prinsa exceptia ThreadAbort, thread-ul se va opri imediat dupa ce termina linia de cod actuala de executat. Pot sa apara inconsistente in date (ex: oprirea s-a facut intre IsValid si IsComplete) din cauza ca sistemul de threading nu stie cand un thread poate fi oprit fara probleme.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

18

static void AcestThread() { Thread.BeginCriticalRegion();


OClasa.IsValid=true; OClasa.IsComplete=true;

Thread.EndCriticalRegion(); OClasa.WriteToConsole(); }

Pentru a rezolva problema vom crea o regiune critica timp in care threadul nu poate fi oprit. Daca am primit o exceptie ThreadAbort cat suntem in interiorul regiunii critice aceasta exceptie este ignorata pana cand termina regiunea critica. Intrarea in regiunea critica se face cu Thread.BeginCriticalRegion(); si iesirea cu Thread.EndCriticalRegion();
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

19

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

20

Fiecare thread are asociat un context de executie. Acesta include:


Informatii de securitate (proprietarul thread-ului si identitatea acestuia) Setarile de localizare (cultura in care ruleaza threadul) Informatii de transactie (din System.Transactions)

Vom discuta despre toate acestea in capitolele urmatoare.


5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

21

In mod normal ExecutionContext curge (se transmite) intre thread-urile create de aplicatia noastra Pentru a creste performanta putem opri curgerea:
ExecutionContext.SuppressFlow(); ExecutionContext.RestoreFlow();

Puteti folosi ExecutionContext si pentru a rula cod arbitrar intr-un thread. Vom folosi metoda Run si metoda ExecutionContext.Capture;
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

22

ExecutionContext ctx = ExecutionContext.Capture(); ExecututionContext.Run(ctx, new ContextCallback(ContextCalled), null);

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

23

Cea mai mare problema ce apare atunci cand lucram cu threaduri este impartirea de date. Aceasi data poate sa fie accesata (si modificata) simultan de 2 sau mai multe threaduri, cauzand inconsistente.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

24

public class Counter { public static int count; } static void UpdateCount() { for (int i=0;i<10000;++i) { Counter.count ++; } } Inainte de threading dupa executarea metodei UpdateCount Counter.count ar fi avut valoare 10000.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

25

ThreadStart starter = new ThreadStart(UpdateCount); Thread[] threaduri = new Thread[10]; Counter.count = 0; for (int i=0;i<10;i++) { thread[i] = new Thread(starter); thread[i].Start(); } for (int i=0;i<10;i++) { threaduri[i].Join(); } Console.WriteLine(Counter.count);
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

26

Daca rulati acest cod pe o calculator singlecore si fara hyperthreading sunt sanse ca rezultatul sa fie corect. Daca in schimb rulati o masina multicore/hyperthreading rezultatul va fi in general gresit. Problema apare deoarece operatia ++ nu este atomica.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

27

Cum putem face operatia ++ atomica?: Folosind clasa Interlocked. Clasa Interlocked are metodele Add, Decrement, Exchange, Increment, Read, toate operatii atomice. ex: in locul liniei Counter.count++ vom scrie linia Interlocked.Increment(ref Counter.count);
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

28

Rezultatul va fi corect. Problema este ca nu putem folosi clasa Interlocked decat pentru un numar mic de tipuri de date.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

29

public class Counter { public static int count; public static int countPar; }

static void UpdateCount() { for (int i=0;i<10000;++x) { Interlocked.Increment(ref Counter.count); if (Counter.count%2==0) Interlocked.Increment(ref Counter.countPar); } }

Cand rulam acest cod count-ul simplu va f corect, dar count-ul de numere pare va fi uneori gresit. Asta se intampla deoarece 2 threaduri pot veni si updata countul simplu unul dupa altul iar verificarea Counter.count%2 sa piarda un increment.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

30

Pentru a rezolva aceasta problema C# are syncronization locks (reprezentate de cuvantul cheie lock). Metoda UpdateCount va deveni: public class forLock { } static forLock fl = new forLock(); static string ana = "ana"; static void UpdateCount() { lock(fl) //putem folosi orice instanta a oricarei clase pentru a realiza lockul: lock(ana) { for (int i=0;i<10000;++x) { Interlocked.Increment(ref Counter.count); if (Counter.count%2==0) Interlocked.Increment(ref Counter.countPar); } } }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

31

Un syncronization lock blocheaza accesul altor threaduri la zona din lock atat timp cand un thread este acolo. Lock() este cea mai simpla metoda in de a sincroniza 2 threaduri in C# O metoda mai complexa dar mai versatila este folosirea unui monitor (reprezentat in c# de clasa Monitor);
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

32

static void UpdateCount() { Monitor.Enter(ana); try { for (int i = 0; i < 10000; ++i) { Interlocked.Increment(ref Counter.count); if (Counter.count % 2 == 0) Interlocked.Increment(ref Counter.countPar); } } finally { Monitor.Exit(ana); } } Clasa monitor are 4 metode: Enter, Exit, TryEnter, Wait
Enter: Creaza un lock exclusiv pe obiectul respectiv Exit: Elibereaza lockul exclusiv TryEnter: Incearca sa creeze un loc exclusiv. Poate sa aiba ca parametru un timp de timeout. Wait: Elibeaza lock-ul exclusiv dar blocheaza threadul actual pana acesta reprimeste lock-ul exlusiv

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

33

Deadlock (interblocaj) este cazul cand 2 bucati de cod incearca sa acceseze un obiect dar se blocheaza una pe cealalata.

class Deadlocker { object ResursaA = new Object(); object ResursaB = new Object(); public void First() { lock (ResursaA) { lock(ResursaB) { Console.WriteLine(First);

Avem deadlock in urmatorul caz: Deadlocker deadlock = new Deadlocker(); ThreadStart firstStart = new ThreadStart(deadlock.Second); ThreadStart secondStart = new ThreadStart(deadlock. Second); Thread first = new Thread(firstStart); Thread second = new Thread(secondStart); first.Start(); second.Start(); first.Join(); second.Join(); Deadlock-ul apare in urmatorul fel: 1> Primul thread porneste si blockeaza ResursaA 2> Al doilea thread porneste si blockeaza ResursaB 3> Primul thread se blocheaza cerand ResursaB care e blocata 4> Al doilea thread se blocheaza cerand ResursaA 5> Aplicatia decedeaza.

}
} } public void Second() { lock (ResursaB) { lock(ResursaA) {

Console.WriteLine(Second);
} } } }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

34

C# nu ofera o moditalitate exacta de verificare de existenta a unui deadlock Totusi puteti folosi metoda Monitor.TryEnter si un timeout. Pentru mai multe informatii vedeti cursul de SO anul 3 Calculatoare.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

35

Pe langa monitor, in C# mai sunt disponibile urmatoarele metode de sincronizare


clasa ReaderWriterLock obiecte kernel Windows
clasa Mutex clasa Semaphore clasa AutoResetEvent clasa ManualResetEvent
.NET ADF Lab4 (c) Victor Adrian Prisacariu

5/30/2013

36

Exista resurse ce pot fi scrise de un singur scriitor dar pot fi citite de mai multi simultan (dar cand scriitorul scrie ceva, un cititor nu poate citi) Clasa ReaderWriterLock ofera o modalitate de a diferentia intre cititori si scriitori pt un obiect. Proprietati:
IsReaderLockHeld> returneaza un indicator ce ne spune daca exista lock reader IsWriterLockHeld > returneaza un indicator ce ne spune daca exista lock writer
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

37

Metode:
AcquireReaderLock: creaza un lock de reader. Metoda primeste ca parametru un timp de timeout in care va fi cerut lock-ul. Daca nu este primit in acest timp este aruncata o exceptie AcquireWriterLock: creaza un lock de writer. Metoda primeste ca parametru un timp de timeout in care va fi cerut lock-ul. Daca nu este primit in acest timp este aruncata o exceptie DowngradeFromWriterLock: writer lock devine reader lock UpgradeToWriterLock: reader lock devine writer lock ReleaseReaderLock: elibereaza reader lock ReleaseWriterLock: elibereaza writer lock
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

38

ReaderWriterLock rwLock = new ReaderWriterLock(); int counter=0; try { rwLock.AcquireReaderLock(100); try { Console.WriteLine(counter); } finally { rwLock.ReleaseReaderLock(); } } catch (ApplicationException) { Console.WriteLine(nu am primit readerlock); }

ReaderWriterLock rwLock = new ReaderWriterLock(); int counter=0; try { rwLock.AcquireWriterLock(1000); try { Interlock.Incerement(ref counter); } finally { rwLock.ReleaseWriterLock(); } } catch (ApplicationException) { Console.WriteLine(nu am primit writerlock); }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

39

Daca dorim ca un cititor sa devina temporar scriitor (sau invers) vom folosi clasa LockCookie:

try { LockCookie cookie = rwLock.UpgradeToWriterLock(1200); counter++; rwLock.DowngradeFromWriterLock(cookie); { catch (ApplicationException) { //nu am putut deveni scriitor deci nu scriem }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

40

La nivelul sistemului de operare exista 3 obiecte kernel (Mutex, Semaphore, Event) care au rolul de a ajuta in sincronizarea intre threaduri. Desi aceastea oferta facilitati puternice ele au viteza mica.Ex: clasa Mutex merge de circa 33 de ori mai incet decat lock. Avantajul principal al lor este ca putem sincroniza threaduri peste granita de proces sau AppDomain (mai clar putem sincroniza acces-ul intre procese). Fiecare obiect kernel are o clasa in c#. (Mutex, Semaphore, AutoResetEvent si ManualResetEvent). Toate acestea deriva din clasa WaitHandle.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

41

Mutex: Permite sincronizarea de tip lock peste granita de AppDomain si proces Semaphore: este folosit pentru a restrictiona accesul la o resursa protejata in functie de numarul de threaduri ce o acceseaza Event: folosit pentru a notifica threaduri din mai multe AppDomain-uri sau procese ca un eveniment a avut loc
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

42

Clasa Mutex functioneaza exact ca un lock simplu, numai ca un astfel de lock este vizibil in toate procesele din sistem si in toate AppDomain-urile din procese. Folosire Mutex:
1> Se creaza o instanta a clasei Mutex: Mutex m = new Mutex(); 2>In interiorul unui thread creati un if care apeleaza metoda WaitOne din mutex-ul creat, pana cand lock-ul asupra obiectului devine disponibil. if (m.WaitOne(1000,false)) //asteapta o secunda { } 3> puneti un try-finally in acest if iar in finaly eliberati mutex-ul
m.ReleaseMutex();

4> optional creati un else la acest if in care sa tratati cazul in care nu s-a primit lock-ul.

Puteti folosi metoda OpenExisting pentru a deschide un Mutex deja existent. Aceasta metoda arunca o exceptie daca nu a fost gasit Mutex-ul.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

43

Un semafor functioneaza foarte asemanator cu un mutex. Singura diferenta este ca el permite accesul mai multor thread-uri la resursa. Un semafor are un numar fix de sloturi in care pot intra thread-urile. Daca un thread gaseste un slot liber va putea accesa resursa. Daca un thread nu a primit accesul se blocheaza in o coada. Fiecare semafor are o coada proprie de thread-uri blocate.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

44

Creare semafor: Semaphore semafor = new Semaphore(0,10); primul parametru este numarul initial de sloturi ocupate si al doilea numarul maxim de sloturi ce pot fi ocupate Eliberare resurse: semafor.Release(5); // numarul de sloturi pe care il elibereaza. Puteti folosi metoda OpenExisting ca si la Mutex.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

45

Un event este un obiect kernel care poate avea 2 stari: ON si OFF. Cand se modifica starea toate threadurile din sistem pot observa ca aceasta stare s-a modificat. Sunt 2 tipuri de event-uri: AutoResetEvent si ManualReserEvent. AutoResetEvent este similar cu Mutex-ul. In momentul in care un event a devenit ON primul thread care observa schimbarea il trece pe OFF. ManualResetEvent este similar cu Semaphore-ul. In momentul in care eventul devine ON toate threadurile care asteapta schimbarea vor detecta ON-ul si nu vor trece eventul pe OFF. Ambele clase mostenesc clasa EventWaitHandle.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

46

AutoResetEvent are = new AutoResetEvent(true); ManualResetEvent mre = new ManualResetEvent(true); are.Set(); mre.Reset();

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

47

Ca orice obiect kernel level si acestea sunt disponibile la toate thread-urile din sistem. Suportul pentru nume este disponibil in clasa EventWaitHandle (nu in AutoResetEvent si nici in ManualResetEvent); ex: EventWaitHandle event = null; try { event = EventWaitHandle.OpenExisting(THEEVENT); } catch (WaitHandleCannotBeOpenedException) { //nu exista event-ul }

if (event==null) // nu l-am deschis .. deci nu exista { event = new EventWaitHandle(false,EventResetMode.AutoReset,THEEVENT); }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

48

Modelul de programare asincron permite unor portiuni de cod sa fie executate in thread-uri separate, fara a fie nevoie de crearea explicita a unui thread nou. Multe clase in .NET freamework au metode cu numele .Begin**** si .End****. Ex: FileStream.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

49

byte [] buffer = new byte[100]; FileStream fs = new FileStream(test.mp3, FileMode.Open, FileAcces.Read, FileShare.Read, 1024, FileOptions.Asyncronous); IAsyncResult result = fs.BeginRead(buffer, 0,buffer.Length, null, null); /// aici putem face ceva independent de citire ///APM int numBytes = fs.EndResult(result); fs.Close();
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

50

APM-ul este reprezentat de metodele BeginRead si EndRead BeginRead seamana mult cu un Read normal:
void Read(byte[] array, int offset, int count); voir BeginRead(byte[] array, int offset, int numBytes, AsyncCallback callback, object stateObject);

Metoda EndRead opreste operatia asincrona. Cand apelati metoda EndRead cu parametrul IAsyncResult aceasta va returna nr de octeti cititi.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

51

Exista 3 modalitati de a trata momentul in care se termina citirea:


Wait-Until-Done Polling Callback

Wait-Until-Done: Dupa ce ati ponit operatia asincrona cu metoda Begin**** executati ce operatii vreti simultan cu executarea metodei. Dupa ce ati terminat operatiile executati metoda End*****. Daca operatia asincrona nu s-a terminat veti astepta pana aceasta se termina si rezultatul il veti primi ca valoare returnata de EndRead. Asa functioneaza exemplul anterior
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

52

In momentul in care apelam BeginRead parametri callback si state object sunt null. Pooling:Modelul pooling este similar, cu exceptia ca vom verifica periodic in IAsyncResult daca operatia s-a terminat, deci nu ne vom bloca la End*****. while (!result.IsCompleted) { //executam o actiune cat timp nu s-a terminat operatia asincrona } Proprietatea IsCompleted in obiectul IAsyncResult ne spune daca s-a terminat sau nu operatia asincrona
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

53

Callback: Pentru a folosi modelul callback este nevoie sa specificam ca parametru a functia Begin***** o metoda ce va fi apelata la finalul operatiei asincrone. Aceasta metoda va primii obiectul stateObject

IAsyncResult result = fs.BeginRead(buffer, 0,buffer.Length, new AsyncCallback(Complet), fs); static void Complet (IAsyncResult result) { Console.WriteLine(S-a terminat citirea); FileStream fs = (FileStream) result.AsyncState; int numBytes = fs.EndRead(result); fs.Close(); }
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

54

Exceptiile in APM pot fi tratate la apelarea metodei End*****.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

55

.NET framework contine un mecanism automat de creare si management al threadurilor, numit ThreadPool Tot ce trebuie sa faceti pentru a folosi ThreadPool-ul este sa apelati metoda QueueWorkItem. Aceasta primeste ca parametru un obiect de tip WaitCallback, un delegat ce pointeaza la o metoda cu un singur parametru de tip object si care nu intoarce nici o valoare. ex: WaitCallback workItem = new WaitCallback(CuParametru); if (!ThreadPool.QueueWorkItem(workItem, ThreadPooled); Console.WriteLine(NU am introdus thread in threadpool);

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

56

Folosirea unui ThreadPool este mai rapida decat creare manuala a threadurilor, deoarece threadurile pot fi refolosite. Cum un thread devine disponibil ThreadPool-ul ii da un nou work item. Un ThreadPool are urmatoarele metode disponibile (printre altele):
GetAvailableThreads GetMaxThreads GetMinThreads QueueUserWorkItem RegisterWaitForSingleObject: Permite apelarea unui callback pentru un anumit WaitHandle cand acel WaitHandle este semnalizat. SetMaxThreads SetMinThreads

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

57

In general nu este nevoie sa limitati numarul de threaduri in un ThreadPool. Pot sa apara 2 cazuri in care doriti sa schimbati setarile implicite:
Infometarea: Aplicatia voastra foloseste un ThreadPool dar acesta functioneaza incet pentru ca are prea multe work items in lucru. Pentru a reduce numarul de work items pe care il accepta vom schimba numarul maxim de threaduri acceptate prin apelul metodei SetMaxThreads Al doilea caz este acela cand timpul de pornire a unui thread este prea mare. Crescand numarul minim de threaduri ce sunt gestionate de ThreadPool va creste numarul de threaduri nu vor astepta terminarea unui operatii aflata deja in executie
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

58

Metodele GetMax,GetMin, SetMax si SetMin Thread primesc sau intorc un parametru de tip completion port. Un completion port este un thread kernel-level folosit pentru operatii de I/O cu fisiere

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

59

Cum am vazut mai devreme exista obiecte la nivel de nucleu pe care le putem folosi pentru sincronizarea si schimbul de date intre threaduri (Mutex, Semaphore, Event), si ca acestea toate mostenesc clasa WaitHandle. ThreadPool permite si el folosirea obiectelor de tip WaitHandle pentru sincronizarea threadurilor. In momentul in care se face o schimbare in Mutex, Semaphore, Event va fi declansat un callback.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

60

Inregistrea callback-ului ce va fi declansat se face prin metoda RegisterWaitForSingleObject

ex: ThreadPool.RegisterWaitForSingleObject (mutex, new WaitOrTimerCallback(MutexHasFired), null, TimeOut.Infinite, true); mutex.ReleaseMutex(); // apelam aceasta metoda pentru a declasa threadul. static void MutexHasFired(object state, bool timeOut) { if (timeOut) { Console.WriteLine(Mutex time out); } else { Console.WriteLine(Mutex semnalizat } }

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

61

Modelul de thread in Windows Forms este diferit de cel in ASP.NET. In timp ce Windows Forms prefera ca majoritatea operatiilor sa aiba loc in threadul main user interface, ASP.NET foloseste un ThreadPool. Pentru a putea scrie cod optim indiferent de platorma pentru care dezvoltati veti folosi un SyncronizationContext.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

62

SyncronizationContext are 2 metode: Send si Post. Metoda Send porneste in executie o metoda cu un parametru care nu intoare nici o valoare si asteapta terminarea acesteia. Send poate sa ruleze metoda in threadul curent sau in alt thread. Metoda Post porneste executia unei metode de acelasi tip dar nu blocheaza executia daca rularea se va face pe alt thread. Altfel executia este blocata.
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

63

SyncronizationContext ctx = SyncronizationContext.Current; ctx.Send(RunMe, Hi); cts.Post(RunMe,Hi);

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

64

Un timer este un obiect care lanseaza asincron metode in executie la intervale de timp. Cand creati un timer specificati un delegat TimerCallback care pointeaza la o metoda ce se va rula cand valoarea trece perioada de timp precizata timerului. In plus puteti preciza dupa cat timp va pornii timerul .NET framework are mai multe timere:
System.Threading.Timer System.Windows.Forms.Timers System.Timer.Timer
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

65

Timer tm = new Timer(new TimerCallback(TimerTick), null, 0, 1000); //timer-ul porneste imediat si suna la 1 sec static void TimerTick(object state) { Console.WriteLine(tic tac); } Timerul suporta metoda Change prin care puteti schimba momentul in care se porneste timerul si cat timp dureaza pana intram in metoda precizata in delegatul callback. tm.Change(Time.Infinite, 1000);
5/30/2013 .NET ADF Lab4 (c) Victor Adrian Prisacariu

66

Sa se realizeze o aplicatie multithreaded care sa citeasca dintr-un director toate fisierele cu extensia .txt si sa le concateneze intr-un singur fisier specificat de catre utilizator Observatii: Pe langa thread-ul procesului curent (automat creat de catre sistemul de operare la rularea aplicatiei), vom mai avea inca 3 thread-uri, dupa cum urmeaza: 1) Un thread care citeste fisierele text din director, care dupa fiecare citire asteapta eliberarea unuia din alte 2 thread-uri (functionand asincron fata de acestea) 2) Un grup de 2 thread-uri care sa primeasca informatiile citite si sa incerce sa scrie in fisierul comun (shared) specificat de catre utilizator. DOAR UNUL din aceste 2 fisiere va primi la un moment dat informatiile de la thread-ul de citire, si doar unul dintre ele va scrie la un moment dat in fisierul partajat Nu conteaza structurile de sincronizare folosite.

5/30/2013

.NET ADF Lab4 (c) Victor Adrian Prisacariu

67