Sunteți pe pagina 1din 67

7

Cercul de programare .NET ADF


1
 In momentul in care este lansat in executie un program sistemul de operare
creaza un proces.
 Procesul este, deci, o instanta a unui program sau mai bine zis un program in
executie.
 Sistemele de operare multitaskig 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.

2
3
 Un thread (fir de executie) este o modalitate de a impartii 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. Procesoul are mai multe core-uri sau
sistemul este multiprocesor.
 Sistemele de operare noi pot implementa ambele tipuri de
multithreading simultan prin intermediul unui scheduler.

4
 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: Thredurile singure renunta la executie
 Threadurile in un 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
6
 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 in 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.

7
 In C# vom folosii pentru a lucra cu thread-uri clasa Thread.
 Proprietati:
 IsAlive: precizeaza daca threadul este in executie sau nu
 IsBackground: precizeaza daca threadul este rulat in background sau
nu
 IsThreadPoolThread: prezieaza daca threadul este in thread pool
 Name: numele thread-ului
 Priority: prioritate
 ManagedThreadId: Un id unic alocat de sistem pentru thread
 ThreadState: Aborted, AbordRequested, Background, Running,
Stopped, StopRequested, Suspended, SuspendedRequested,
Unstarted, WaitSleepJoin

8
 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 folosii
 Start: porneste thread-ul

9
 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.
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

11
static void SimpleWork()
{
Console.WriteLine(“Thread {0}”,
Thread.CurrentThread.ManagedThreadId;
}

ThreadStart operatie = new ThreadStart(SimpleWork);

Thread threadul = new Thread(operatie);

threadul.Start();

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.

13
 Putem influenta modul in care schedulerul programeaza
thread-urile modificat prioritatea acestora.
 Pentru a modifica prioritatea unui thread modificam
prioritatea Priority una din valorile din enumerarea
ThreadPriority:
 HighPriority
 AboveNormal
 Normal
 BelowNormal
 Lowest
 In general nu veti creste performanta unui thread prin
cresterea prioritatii acestuia.
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
folosii un nou delegat numit
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).

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”);

16
 Mecanismul princiapal de oprire a unui thread
este apelarea metodei .Abort();
 Cand este apelata aceasta metoda systemul de
threading arunca in thread o exceptie de tip
ThreadAbortException.
 Indiferent daca exceptia este prinsa sau nu
thread-ul este oprit.

17
static void AcestThread()
{
OClasa.IsValid=true;
OClasa.IsComplete=true;
OClasa.WriteToConsole();
}

Thread threadul = new Thread(new ThreadStart(AcetThread));


 In momentul in care trimite abort la acest thread, din cauza ca nu este prinsa
exceptia ThreadAbort, thread-ul se va oprii 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.

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();

19
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.
21
 In mod normal ExectuionContext “curge”  spre
thread-urile create de aplicatia noastra
 Pentru a creste performanta putem oprii “curgerea”:
 ExecutionContext.SuppressFlow();
 ExecutionContext.RestoreFlow();
 Puteti folosii ExecutionContext si pentru a rula cod
arbitrar in un thread.
 Vom folosii metoda Run si metoda
ExecututionContext.Capture;

22
ExecutionContext ctx =
ExecutionContext.Capture();

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

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

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.

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);

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.
27
 Cum putem face operatia ++ atomica?: Folosind
clasa Interlocked.
 Clasa interlocked are metodele Add, Decrement,
Exchange, Increment, Read, toate operatii
atomica.
 ex: in locul liniei Counter.count++ vom scrie linie
Interlocked.Increment(ref Counter.count);

28
 Rezultatul va fi corect.
 Problema este ca nu putem folosii clasa
interlocked decat pentru un numar mic de tipuri
de date.

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 venii si updata countul simplu unul dupa altul iar verificarea
Counter.count%2 sa piarda un increment.

30
 Pentru a rezolva aceasta problema C# are syncronization locks (reprezentate de cuvantul cheie lock).
 Metoda UpdateCount va devenii:

public class forLock { }


static forLock fl = new forLock();
static string ana = "ana";
static void UpdateCount()
{
lock(fl) //putem folosii 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);
}
}
}

31
 Un syncronization lock blocheaza accesul altor
threadurila la zona din lock atat timp cand un
thread este acolo.
 Lock este cea mai simpla metoda in c# de a
sincromiza 2 threaduri.
 O metoda mai complexa dar mai versatila este
folosirea unui monitor (reprezentat in c# de clasa
monitor);
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

33
 Deadlock (interblocaj) este cazul cand 2 bucati de cod incearca sa acceseze un obiect dar se
blocheaza una pe cealalata.
Avem deadlock in urmatorul caz:
class Deadlocker
{
Deadlocker deadlock = new Deadlocker();
object ResursaA = new Object();
ThreadStart firstStart = new ThreadStart(deadlock.Second);
object ResursaB = new Object();
ThreadStart secondStart = new ThreadStart(deadlock. Second);
public void First()
Thread first = new Thread(firstStart);
{
Thread second = new Thread(secondStart);
lock (ResursaA)
first.Start();
{
second.Start();
lock(ResursaB)
first.Join();
{
second.Join();
Console.WriteLine(“First”);
}
Deadlock-ul apare in urmatorul fel:
}
1> Primul thread porneste si blockeaza ResursaA
}
2> Al doilea thread porneste si blockeaza ResursaB
public void Second()
3> Primul thread se blocheaza cerand ResursaB care e blocata
{
4> Al doilea thread se blocheaza cerand ResursaA
lock (ResursaB)
5> Aplicatia decedeaza.
{
lock(ResursaA)
{
Console.WriteLine(“Second”);
}
}
}
} 34
 C# nu ofera o moditalitate exacta de verificare
de existenta deadlock
 Totusi puteti folosii metoda Monitor.TryEnter si
un timeout.
 Pentru mai multe informatii vedeti cursul de SO
anul 3 Calculatoare.

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

36
 Exista resurse ce pot fi scrise de un singur scriitor dar
pot fi citite de mai multi simultan (dar cand scriitorul
scrie niic un cititor nu poate citii)
 Clasa ReaderWriterLock ofera o modalitate de a
diferentia intre cititori si scriitori la 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
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

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

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

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

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 mere de appox 33 de ori mai incet
decat lock.
 Avantajul lor principal este ca putem sinconiza threaduri
peste granita de process sau AppDomain-uri (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.
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
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.

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.

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 folosii metoda OpenExisting ca si la Mutex.
45
 Un event este un obiect kernel care poate avea 2 valori: ON si
OFF.
 Cand se modifica starea toate threadurile din sistem pot
observa ca aceasta stare s-a modificat.
 Sunt 2 tipuri de event-rui: 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.
46
AutoResetEvent are = new AutoResetEvent(true);
ManualResetEvent mre = new
ManualResetEvent(true);
are.Set();
mre.Reset();

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”);
}

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.

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();

50
 APM-ul este reprezentat de metodele BeginRead si
EndRead
 Begin read seamana mult cu o functie Read normala:
 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 octetii cititi.
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 primii ca
valoare returnata de EndRead.
 Asa functioneaza exemplul anterior

52
 In momentul in care apelam BeginRead parametrii 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

53
 Callback: Pentru a folosii 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();
}

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

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 intoace nici o valoare.
ex:
WaitCallback workItem = new WaitCallback(CuParametru);
if (!ThreadPool.QueueWorkItem(workItem, “ThreadPooled”);
Console.WriteLine(“NU am introdus thread in threadpool”);

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

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

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

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.
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”
}
}

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 dezolvati veti folosi un
SyncronizationContext.
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 da nu blocheaza executia daca rularea se
va face pe alt thread. Altfel executia este blocata.

63
SyncronizationContext ctx =
SyncronizationContext.Current;

ctx.Send(RunMe, “Hi”);
cts.Post(RunMe,”Hi”);

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

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 putei schimba momentul in


care se porneste timerul si cat timp dureaza pana intram in metoda
precizata in delegatul callback.

tm.Change(Time.Infinite, 1000);
66
67

S-ar putea să vă placă și