Sei sulla pagina 1di 7

Ing.

Gabriele MONTI 2012

C# threads

www.ingmonti.it

Programmazione concorrente in C#

Il linguaggio C#, ro osto da Mi!roso"t intorno all#anno 2000, "a uso delle librerie del $"ramewor% .N&T$ ed utili''a un a ro!!io a $ma!!hina (irtuale$ is irato a )uello di *a(a. Il !om ilatore C#, !ome )uello di +isual,asi! .N&T e di altri linguaggi basati sul $"ramewor%$, reali''a un !odi!e eseguibile er una C./ $(irtuale$, !he non esiste. Il linguaggio er tale $C./$, !he si !hiama 0CI1 $Common Intermediate 1anguage$2, ed 3 stato ensato er oter essere generato a artire da molti linguaggi di(ersi. 1a ma!!hina (irtuale 3 un inter rete, !he ha il !om ito di tradurre le istru'ioni da linguaggio CI1 al linguaggio ma!!hina della C./ $os ite$ e di "ar eseguire il rogramma !os4 ottenuto. Il nome della ma!!hina (irtuale $.N&T$ 3 $C15$ 0Common 1anguage 5untime2. Il "ramewor% .N&T "a am io uso dei thread e lo stesso linguaggio C# ha istru'ioni s e!i"i!he er la !rea'ione di thread indi endenti ed an!he er la loro sin!roni''a'ione. In )uesto !a itolo si tratta solo delle !aratteristi!he di C# !he riguardano il $multithreading$, dando er s!ontato $il resto$.

Thread non interagenti


6n!he )uando non 3 indis ensabile !he due rogrammi !omuni!hino "ra loro, u7 essere !on(eniente "arli eseguire !ontem oraneamente, er maggiore e""i!ien'a o sem li!it8 di reali''a'ione. In C# un rogramma u7 lan!iate nuo(i thread !he, ur !ondi(idendo lo stesso s a'io di memoria del rogramma !he lo !rea, hanno $(ita ro ria$, indi endente, dal unto di (ista dell#ese!u'ione. &ssi hanno inoltre il ro rio sta!% di memoria, nel )uale ossono !reare tutte le (ariabili lo!ali di !ui abbisognano. 9e i thread !reati non interagis!ono, non !i sono arti!olari roblemi di rogramma'ione, !ome gi8 anti!i ato nel !a itolo generale sulla rogramma'ione !on!orrente. 6n!he se si otrebbe non a re''arlo $dall#esterno$, tutti i rogrammi C# sono !omun)ue multithreaded: molto s esso, durante la loro ese!u'ione, le librerie o la ma!!hina (irtuale !reano nuo(i thread, !he eseguono in arallelo al rogramma rin!i ale. 1a ossibilit8 di !reare nuo(i thread 3 data an!he al rogrammatore$normale$, !on le le istru'ioni !he andiamo ad introdurre. Innan'itutto bisogna dire !he tutte le !lassi !he !i ermettono di usare il multithreading "anno arte del names a!e $9;stem.Threading$, er !ui all#ini'io di ogni modulo di rogramma !he usa i thread do(remo di!hiarare<
using System.Threading;

/n thread C# 3 un oggetto di !lasse Thread, !he (a !reato !on il !ostruttore, !ome una normale istan'a di !lasse Thread. Il !ostruttore della Classe Thread (uole !ome arametro il nome del metodo !he si (uole eseguire in modo !on!orrente. Cos4 sar8 ossibile sa ere )uale rogramma "ar artire !ontem oraneamente ad altri. .er "ar e""etti(amente artire il thread 3 ne!essario !hiamare il metodo 9tart dell#oggetto Thread a ena !reato. 9intassi della !rea'ione di un thread: un#istan'a di )uesta !lasse (iene !reata, !ome er tutte le istan'e, !on $new$< Thread <nome oggetto thread> = new(<nome Metodo>); .er "ar artire il metodo <nome Metodo> !ome thread indi endente, basta lan!iare il metodo start dell#oggetto thread< <nome oggetto thread>.Start(); &sem io<
using System.Threading; namespace ThreadSemplice { class ThreadSemplice { static void Main() { Thread t = new Thread(ContaDaMeno); istan!ia un oggetto t di classe Thread indicando "uale metodo deve essere lanciato allo start

// fa partire <nome Metodo> come thread

0=b rogr !on!orrente C#.odt

+er. 0.1.0 2012-0>-1?

. 1 di =

Ing. Gabriele MONTI


t.Start(); il metodo Start #a partire il Thread t$ che esegue C%&T'M(%)*&'*M'&T' al main il main visuali!!a delle * #or (int i = +; i , -+++; i..) Console./rite(i .0 0); Console.)ead1ine();

www.ingmonti.it

2 static void ContaDaMeno() { ciclo da -+++ giri$ con i che parte da 3-+++ e #inisce a + #or (int i = 3-+++; i , +; i..) Console./rite(i . 0 0); 2 2 2

@uesto esem io "a (edere $mis!hiati$ gli out ut dei due thread, !he !om etono nell#a!!esso alla C./. /no !onta da 0 a 1000, e (isuali''a il !onteggio, l#altro da -1000 a 0. Il risultato 3 una se)uen'a di "asi nelle )uali (engono (isuali''ati numeri negati(i, seguite da "asi !on numeri ositi(i. +olendo "ar artire un ter'o Thread, basta aggiungere la !rea'ione di un altro Thread e lan!iarlo. 9e, er esem io, aggiungiamo )uesto metodo al !odi!e re!edente<
static void *l#a4eti() { ciclo da 5+ al#a4eti #or (int i = +; i , 67+; i..) { char c = 8a8; while (c , 8!8) { Console./rite(c . 0 0); c..; 2 2 2

&, nel rogramma rin!i ale aggiungiamo la !rea'ione ed il lan!io del Thread<
Thread t- = new Thread(*l#a4eti); istan!ia l8oggetto t- di classe Thread t-.Start(); il metodo Start partire *l#a4eti()

Nell#out ut a aiono, ogni tanto, an!he dei e''i di al"abeto minus!olo. 9egno !he an!he )uesto ter'o Thread gira !ontem oramente agli altri due. @uando il rogramma !he esegue nel thread 3 stato e""etti(amente lan!iato e er tutto il tem o in !ui il thread esegue, la sua ro riet8 Is6li(e ha (alore true. /n thread "inis!e )uando "inis!e di eseguire il suo !odi!e. Nell#esem io re!edente il thread rin!i ale "inis!e )uando 3 arri(ato a 1000 nel suo !onteggio, il se!ondo thread )uando 3 arri(ato a 'ero, il ter'o )uando ha "inito di stam are A0 al"abeti minus!oli. Ogni thread aggiunto in un rogramma C# ri!hiede !ir!a 1 M,;te di memoria addi'ionale er "un'ionare.

1.0.1

Metodi e propriet principali per i thread C# che non si sincronizzano


metodo che #a partire il thread #a sospendere il thread corrente per lasciare l8esecu!ione agli altri. 18esecu!ione riprende con )esume() #a riprendere l8esecu!ione del thread che era stato sospeso

Metodi iB im ortanti
,oggetto thread9.)un() ,oggetto thread9.Suspend() ,oggetto thread9.)esume()

.ro riet8
,oggetto thread9.:s*live propriet; di tipo 4ool che indica se il thread < operativo ,oggetto thread9.(riority propriet; enum Thread(riority. :ndica la priorit; di esecu!ione di "uesto thread. 1ettura = scrittura Thread(riority pu> avere "uesti valori? {1owest$@elow&ormal$&ormal$*4ove&ormal$Aighest2 ,oggetto thread9.ThreadState propriet; di classe ThreadState. :ndica lo stato corrente del thread ,oggetto thread9.&ame propriet; di tipo string. '8 il nome dell8oggetto Thread

. 2 di =

+er. 0.1.0 2012-0>-1?

0=b rogr !on!orrente C#.odt

Ing. Gabriele MONTI 2012 Metodi meno usati


,oggetto thread9.Boin()

C# threads

www.ingmonti.it

4locca il thread su cui viene eseguito #ino a che non termina il thread su cui viene applicato. Stato 04locCed0 ,oggetto thread9.Boin(,millisecondi9) come Boin()$ solo che < speci#icato un timeout. Se passa il timeout sen!a che l8altro thread sia #inito il thread che aveva chiamato la Boin() riparte comun"ue ,oggetto thread9.:nterrupt() interrompe un thread il cui stato < 04locCed0 (/aitSleepBoin) ,oggetto thread9.*4ort() #a a4ortire il thread indicato

Metodi stati!i della !lasse Thread @uesti metodi sono stati!i, )uindi (anno !hiamati !on Thread.CmetodoD, ma, se eseguiti all#interno di un Thread, hanno e""etto sul @/&11O 9T&99O thread.
Thread.Sleep (,millisecondi9) sospende il thread che lo esegue per il tempo indicato Thread.CurrentThread < un oggetto di classe Thread. '8 il Thread che sta eseguendo in "uesto istante Thread.Spin/ait(,nDiri9) < un8attesa attiva. :l thread rimane in attesa in un ciclo che dura il numero di volte indicato come parametro Thread.Eield() cede l8esecu!ione ad un altro thread in stato di pronto. :l S.%. sceglie il prossimo thread da eseguire FFF Thread.@eginCritical)egion() Thread.'ndCritical)egion() FFF

1.0.2

Dati del thread

Ei!hiarando (ariabili all#interno del metodo !he (err8 lan!iato !ome thread, )uelle (ariabili a(ranno !ome !am o di (isibilit8 il solo thread. 9e istan'ieremo due thread a artire dallo stesso !odi!e (erranno !reate due (ariabili lo!ali, !on lo stesso (alore ini'iale. /n thread non otr8 (edere nF modi"i!are le (ariabili lo!ali dell#altro. 9e er esem io eseguiamo )uesto !odi!e<
static void Main(stringGH args) { modo rapido per creare ed eseguire un Thread con una sola riga di codice? new Thread(Solitario).Start(); parte Solitario() in un altro thread Solitario(); eseguo Solitario() nel thread del Main (in totale eseguo due thread) Console.)ead1ine(); 2 static void Solitario() { int varia4ile1ocale = -6; Console./rite(varia4ile1ocale . 0 0); varia4ile1ocale..; Console./rite(varia4ile1ocale . 0 0); 2

I due thread (isuali''eranno 12 e 1G, il (alore delle loro due !o ie della (ariabile1o!ale. Il risultato sar8 er!i7<
-6 -I -6 -I

O )ual!he altro in!astro dei numeri 12 e 1G, !i7 di endendo dal !aso.. .ossiamo dun)ue de"inire (ariabili lo!ali nei nostri thread, !on la si!ure''a !he nessun altro (err8 a modi"i!ar!ele.

1.0.3 1.0.4

Array di Thread Comunicazioni ra i thread

HHHH TOEO HHHH

&# robabile !he sia ne!essario !he i thread si s!ambino in"orma'ioni. Ci sono molti modi er "arlo, ma il iB sem li!e 3 de"inire (ariabili !omuni, (isibili "ra tutti i thread interessati, e !he i thread siano in grado di modi"i!are. Il modo iB immediato 3 usare le (ariabili $della !lasse$, !io3 le $stati!$, !he sono le stesse er ogni istan'a della !lasse.
static int contatore = +; static void Main(stringGH args)

0=b rogr !on!orrente C#.odt

+er. 0.1.0 2012-0>-1?

. G di =

Ing. Gabriele MONTI


{

www.ingmonti.it

new Thread(:nteragente).Start(); parte :nteragente() in un altro thread :nteragente(); eseguo :nteragente() nel thread del Main Console.)ead1ine();

2 static void :nteragente() { contatore..; Console./rite(contatore . 0 0); 2

In )uesto rogramma la (ariabile $!ontatore$ 3 stati!, dun)ue 3 !ondi(isa da tutte le istan'e della !lasse. I due thread, !he sono lan!iati !ome nell#esem io re!edente, in!rementano la (ariabile $!ontatore$ e la (isuali''ano. 9i!!ome la (ariabile 3 stati!, essa 3 16 9T&996 er i due thread, er !ui da essi (iene in!rementata e di(enta rima 1, oi 2. Il risultato 3<
- 6

1.0.!

"assare dati ad un thread

Il modo iB sem li!e 3 assare i arametri !he interessano allo 9tart02 del thread. &sem io<
static void Main() { Thread t = new Thread (Scrivi); // passo una stringa con la Start() t Start (%Sono partito$%); &

!n realt" essa # un oggetto$

static void Scrivi (o'(ect messaggio) { string m = (string) messaggio; // ) cast perch# la stringa era stata passata come oggetto *onsole +rite,ine (m); &

@uesto 3 ossibile er!h3 il !ostruttore dell#oggetto Thread $registra$ due delegati er il metodo 9tart del Thread< Thread9tart02 sen'a arametri e .arameteri'edThread9tart 0obIe!t obI2, !he ha un uni!o arametro di ti o obIe!t.

2 Sincronizzazione
2.0.1 Corsa sui dati condi#isi
@uando esistono (ariabili !ondi(ise "ra i thread !i ossono essere !orse. Ja!!iamo un esem io. Nel !odi!e seguente la (ariabile $a(anti&Indr3$ (iene !ondi(isa "ra tutti i metodi della !lasse, er!h3 3 de"inita al di "uori
dei blo!!hi dei metodi. $a(anti&Indr3$ (iene in!rementata dal metodo 6ggiungi02 e de!rementata dal metodo Togli02, !he eseguono !ias!uno er 100 (olte. &ntrambi i metodi si "ermano er un tem o !asuale massimo di ?0 ms. pu4lic class Classe*vanti':ndietro { int avanti':ndr< = +; )andom casu = new )andom(); pu4lic void *ggiungi() { #or (int i = +; i , -++; i..) { int temp = avanti':ndr< . -; Thread.Sleep(casu.&eJt(K+)); avanti':ndr< = temp; Console./rite1ine 2 2 pu4lic void Togli() {

simula un sacco di ela4ora!ioni da #are

. A di =

+er. 0.1.0 2012-0>-1?

0=b rogr !on!orrente C#.odt

Ing. Gabriele MONTI 2012

C# threads

www.ingmonti.it

2 2

#or (int i = +; i , -++; i..) { int temp = avanti':ndr< 3 -; Thread.Sleep(casu.&eJt(K+)); simula un sacco di ela4ora!ioni da #are avanti':ndr< = temp; Console./rite1ine(03T 0 . avanti':ndr< . 0 0); 2

Il seguente rogramma rin!i ale esegue rima i due metodi 6ggiungi02 e Togli02 in stretta se)uen'a, ottenenendo il (alore "inale 0< si aggiunge "ino a 100 e si toglie "ino a 'ero. .oi il Main02 !rea due Thread !he eseguono 6ggiungi02 e Togli02 in modo !on!orrente<
static void Main(stringGH args) { esecu!ione non concorrente$ in se"uen!a Classe*vanti':ndietro oggetto(erThread = new Classe*vanti':ndietro(); oggetto(erThread.*ggiungi(); oggetto(erThread.Togli(); Console.)ead1ine(); esecu!ione concorrente con due Thread Thread t- = new Thread(oggetto(erThread.*ggiungi); t- usa il metodo *ggiungi() Thread t6 = new Thread(oggetto(erThread.Togli); t6 usa il metodo Togli() #a partire i due thread t-.Start(); t6.Start(); attende che i due thread siano #initi t-.Boin(); attende che t- #inisca t6.Boin(); attende che t6 #inisca Console.)ead1ine(); 2

1e !orse resenti in 6ggiungi02 e Togli02, esa!erbate dalla resen'a dei ritardi, !ausano enormi roblemi di in!onsisten'a della (ariabile a(anti&Indr3, nell#oggetto oggetto.erThread. Il (alore di $a(anti&Indr3$ 3 sem re errato ed os!illante ed il (alore "inale 3 rati!amente !asuale.

2.0.2 2.0.3

$ezioni critiche loc%

In C# er le se'ioni !riti!he esistono il !ostrutto $lo!%$ ed il $muteK$.

1o!% 3 un metodo !he si a li!a all#oggetto !he gli si assa !ome arametro. Ja in modo !he il blo!!o de"inito al suo interno (enga eseguito in modo mutuamente es!lusi(o. 9intassi: lock(<o'(ect>) = {<'locco da eseguire in mutua esclusione>}; @uando un thread sta eseguendo la se'ione !riti!a de"inita dal !odi!e del <'locco da eseguire in mutua esclusione>, $lo!%$ im edis!e di entrare nella loro se'ione !riti!a al !odi!e di T/TTI gli altri thread !he !i ro(ano. I thread !he tentano di entrare nella loro se'ione !riti!a mentre !e n#3 un altro (engono automati!amente sos esi in una !oda "ino a !he il lo!% non 3 liberato dall#us!ita del thread !he lo blo!!a(a dalla se'ione !riti!a. 1#attesa dei thread blo!!ati su un lo!% 3 assi(a 0non 3 reali''ata !on un !i!lo di olling 0s inlo!%2 e )uindi NON !onsuma risorse della C./2. @uando (iene blo!!ato dalla lo!% lo stato del thread 0Thread9tate2 assa a $Lait9lee *oin$. 9i ri rende l#esem io re!edente 0!onteggio a(anti e indietro2, mostrando di seguito il !odi!e !he risol(e il roblema usando la lo!%<
pu4lic class Classe*vanti':ndietro { int avanti':ndr< = +; )andom casu = new )andom();

0=b rogr !on!orrente C#.odt

+er. 0.1.0 2012-0>-1?

. > di =

Ing. Gabriele MONTI


pu4lic void *ggiungi() { #or (int i = +; i , -++; i..) { locC (this) 4locca la se!ione critica { esegue la se!ione critica avanti':ndr<..; int temp = avanti':ndr< . -; Thread.Sleep(casu.&eJt(K+)); simula un avanti':ndr< = temp; Console./rite1ine(0.T 0 . avanti':ndr< . 0 2 rilascia il locC 2 2 pu4lic void Togli() { #or (int i = +; i , -++; i..) { locC (this) 4locca la se!ione critica { esegue la se!ione critica avanti':ndr<33; int temp = avanti':ndr< 3 -; Thread.Sleep(casu.&eJt(K+)); simula un avanti':ndr< = temp; Console./rite1ine(03T 0 . avanti':ndr< . 0 2 rilascia il locC 2 2

www.ingmonti.it

sacco di ela4ora!ioni da #are 0);

sacco di ela4ora!ioni da #are 0);

1e due se'ioni !riti!he di 6ggiungi02 e di Togli02 sono i blo!!hi delimitati dalle gra""e !he seguono le lo!%0this2. Eato !he l#ese!u'ione mutuamente es!lusi(a delle se'ioni !riti!he 3 assi!urata della lo!%, le !orse sono eliminate. I due Thread 6ggiungi02 e Togli02 eseguono in !on!orren'a e mantengono il (alore della (ariabile $6(anti&Indr3$ (i!ino allo 'ero, da !ui si dis!ostano er i tem i di(ersi do(uti alle attese !asuali. 6lla "ine !omun)ue, )uando entrambi i Thread hanno !om letato la loro ese!u'ione, il (alore di $6(anti&Indr3$ 3 sem re 'ero, a !on"ermare !he il rogramma non ha iB roblemi di !onsisten'a. Nell#esem io la lo!% (iene usata !on l#oggetto $this$ 0lo!%0this22. &# ossibile e si "a, ma iB in genarelae, l#oggetto !he si u7 usare er blo!!are la se'ione !riti!a !on lo!% de(e essere un )ualun)ue ti o $ri"erimento$, !io3 )uei ti o !he memori''ano nella (ariabile l#indiri''o dell#area di memoria !he !ontiene il (alore della (ariabile e non il (alore stesso. +anno er!i7 bene< tutti gli oggetti in generale 0obIe!t e tutti i suoi arenti oggetti 0"igli, $ni oti$, ..22, le stringhe, gli arra;. +anno male tutti i ti i numeri!i e !har. 9olo il thread !he ha eseguito il lo!% u7 liberare la se'ione !riti!a. 1a (ariabile oggetto di sin!roni''a'ione do(rebbe essere ri(ate.

2.0.4

Mute&

MuteK "un'iona !ome lo!%, ma lo u7 "are an!he "ra ro!essi, non solo "ra Thread. @uesto signi"i!a !he una se'ione !riti!a rotetta da un MuteK lo "a a li(ello di 9istema ed il blo!!o (ale er tutti i ro!essi !he girano sul !om uter, non solo er )uelli dell#a li!a'ione !he stiamo s!ri(endo. .er im egnare un MuteK si de(e usare il metodo LaitOne02, mentre lo si libera !on 5eleaseMuteK02. 9e un MuteK (iene !hiuso la 5eleaseMuteK02 (iene e""ettuata automati!amente. Il "un'iomento di un MuteK 3 iB lento di )uello di un lo!%. /n MuteK u7 ser(ire er e(itare !he iB di un#istan'a di un rogramma eseguano !ontem oraneamente. &sem io< HHHH TOEO HHHH Considerando il MuteK !ome un sema"oro mutuamente es!lusi(o, la LaitOne02 !orris onde alla wait02, mentre la 5eleaseMuteK02 3 la signal02. 9olo il thread !he ha im egnato il MuteK u7 liberarlo.

2.0.!

$emaphore

In C# il $9ema hore$ 3 il sema"oro generali''ato, )uello !he limita l#a!!esso ad una se'ione !riti!a solo ad un !erto numero di thread !ontem oranei. . M di = +er. 0.1.0 2012-0>-1? 0=b rogr !on!orrente C#.odt

Ing. Gabriele MONTI 2012

C# threads

www.ingmonti.it

1a wait e la signal sono i stessi metodi del muteK. 6 di""eren'a dei !asi di lo!% e MuteK, usando i sema"ori tutti i thread ossono eseguile la signal02. I sema"ori !on nome, !ome i MuteK, sono $s;stem wide$, la(orando sui ro!essi e non es!lusi(amente sui Thread.

2.0.'

$e(nalazioni )si(nallin(*

HHHH TOEO HHHH Interlo!%ed 5eali''a al!une "un'ioni atomi!he su (ariabili !ondi(ise in modo !on!orrente. 6dd02 somma atomi!a di due integer In!rement02 Ee!rement02 &K!hange202 s!ambio $atomi!o$ "ra i (alori di due (ariabili intere Monitor 3 asso!iabile ad un oggetto &nter02 indi!a l#ini'io del !odi!e di se'ione !riti!a &Kit 02 segnala la "ine dell#ese!u'ione della se'ione !riti!a .ulse02 in(ia ad uno o iB thread !he sono nella !oda di attesa !he lo stato del thread !he blo!!a(a la risorsa 3 !ambiato .ulse6ll02 noti"i!a a tutti Lait02 rilas!ia il blo!!o sull#oggetto ed interrom e il thread !orrente, er "are in modo !he gli altri thread ossano eseguire

2.0.+

Deadloc%

Non !i sono !ostrutti di C# !he ossano rile(are od e(itare il deadlo!%. &# un roblema !he de(e essere risolto dal rogrammatore, s(ilu ando i rogrammi !on!orrenti in modo a!!orto e rudente.

0=b rogr !on!orrente C#.odt

+er. 0.1.0 2012-0>-1?

. = di =

Potrebbero piacerti anche