Thread [email protected] Alessandro … · simultanee in un programma in C#. ......
Transcript of Thread [email protected] Alessandro … · simultanee in un programma in C#. ......
Il linguaggio C#
Thread
Tecniche di Programmazione avanzata
Corso di Laurea Specialistica in Ingegneria Telematica
Università
Kore –Enna –
A.A. 2008-2009Alessandro Longheuhttp://w
ww.diit.unict.it/users/alongheu
2
A. Longheu –Tecniche di programmazione avanzata
�Il
threading consente
di eseguire
elaborazioni sim
ultanee in un programma in C#
. �Lo spazio dei nom
i System.Threading
fornisce classi e interfacce
che supportano
la program
mazione
multithreading
�la creazione e l'avvio di nuovi thread,
�la sincronizzazione di più
thread, �la sospensione e l'interruzione di thread.
�Per incorporare il threading nel codice C#
, èsufficiente
creare una funzione da eseguire all'esterno del thread principale e puntarvi un nuovo oggetto Thread
Thread
3
A. Longheu –Tecniche di programmazione avanzata
Gestione delle risorse
�La strategia più
diffusa consiste nell'utilizzare i thread di lavoro per eseguire attività
lunghe o in cui il tempo
riveste importanza m
a che non richiedono molte delle
risorse utilizzate da altri thread. �lo spazio dei nom
i System.Threading fornisce le classi
per la sincronizzazione dei thread. �Mutex,
�Monitor,
�Interlocked,
�AutoR
esetEvent e ManualR
esetEvent.
4
A. Longheu –Tecniche di programmazione avanzata
Tipi
public sealed class Thread {public static Thread CurrentThread { get; }
// metodi statici
public static void Sleep(int milliSeconds) {...}
...public Thread(ThreadStart startMethod) {...}
// creazione dei threadpublic string N
ame { get; set; }
// proprietàpublic ThreadPriority Priority { get; set; }public ThreadState ThreadState { get; }public bool IsAlive { get; }public bool IsBackground { get; set; }...public void Start() {...}
// metodi
public void Suspend() {...}public void Resum
e() {...}public void Join() {...}
// il chiamante aspetta che il thread term
inipublic void Abort() {...}
// emette ThreadAbortException
public void Interrupt() {...}// chiam
ato solo nello stato WaitSleepState
...}public delegate void ThreadStart();
// metodo senza param
etripublic enum
ThreadPriority {Norm
al, AboveNorm
al, BelowNorm
al, Highest, Low
est}public enum
ThreadState {Unstarted, Running, Suspended, Stopped,Aborted, ...}
5
A. Longheu –Tecniche di programmazione avanzata
Esempio
using System;
using System.Threading;
class Printer {char ch;int sleepTim
e;public Printer(char c, int t) {ch =
c; sleepTime =
t;}public void Print() {for (int i =
1; i < 100; i+
+) {
Console.Write(ch);
Thread.Sleep(sleepTime);
}}
}
class Test {static void M
ain() {Printer a =
new Printer('.', 10);
Printer b = new
Printer('*', 100);new
Thread(new ThreadStart(a.Print)).Start();
new Thread(new
ThreadStart(b.Print)).Start();}}
�Due thread che stam
pano caratteri6
A. Longheu –Tecniche di programmazione avanzata
Confronto con Java
Il metodo stop è
deprecatoThreadAbortException
può essere catturata, m
a èrilanciata alla fine del
gestore, tranne se èinvocato ResetAbort.
Tutti i blocchi finally sono eseguiti.
Bisogna implem
entare il metodo run
Qualsiasi m
etodo di tipo void senza param
etri può essere un thread
I Thread utente sono una estensione di Thread
Non è
necessario estendere Thread
class MyThread extends Thread {
public void run() {... thread actions
...}}
Thread t = new
MyThread();
void P() {... thread actions
...}Thread t =
new
Thread(newThreadStart(P));
Java
C#
7
A. Longheu –Tecniche di programmazione avanzata
Thread
Thread t = new
Thread(new ThreadStart(P));
Console.WriteLine("nam
e={0}, priority=
{1}, state={2}", t.N
ame, t.Priority,
t.ThreadState);t.N
ame =
"Worker"; t.Priority =
ThreadPriority.BelowNorm
al;t.Start();Thread.Sleep(1);Console.W
riteLine("name=
{0}, priority={1}, state=
{2}", t.Nam
e, t.Priority, t.ThreadState);
t.Suspend();Thread.Sleep(1);Console.W
riteLine("state={0}", t.ThreadState);
t.Resume();
Console.WriteLine("state=
{0}", t.ThreadState);t.Abort();Thread.Sleep(1);Console.W
riteLine("state={0}", t.ThreadState);
Outputnam
e=, priority=
Norm
al, state=Unstarted
name=
Worker, priority=
BelowNorm
al, state=Running
state=Suspended
state=Running
state=Stopped
8
A. Longheu –Tecniche di programmazione avanzata
Diagram
ma di stato di un Thread
Running
Unstarted
t.Start
WaitSleepJoin
Thread.Sleepother.JoinWait(obj)
t.Interrupttim
e overother joinedPulse(obj)
SuspendRequested
safepoint reached
Suspended
t.Resume
AbortRequested
t.Abort
Stopped
t.Abort
Exception caught,finally processed
t.Suspendend of
thread method
9
A. Longheu –Tecniche di programmazione avanzata
�Creazione di una classe che esegue il lavoro (Executor)
�Im
plementazione di un m
etodo (void Run())
�Istanza di Executor
�Istanza del Thread inizializzato su Executor
�Esecuzione di start()
Thread
10
A. Longheu –Tecniche di programmazione avanzata
�Modificatore v
olatile
�La parola chiave v
olatile
avvisa il compilatore che più
thread accederanno al mem
bro dati�Il com
pilatore non deve ottimizzare questo m
embro.
�Proprietà
isAlive�Metodo Abort()
�Blocca il thread
�Metodo Join()
�Sincronizza con il thread
�Use this m
ethod to ensure a thread has terminated. The
caller will
block indefinitely
if the
thread does
not term
inate.
Thread
11
A. Longheu –Tecniche di programmazione avanzata
Joinusing System
;using System
.Threading;
class Test {static void P() {for (int i =
1; i <= 20; i+
+) {
Console.Write('-');
Thread.Sleep(100);}
}static void M
ain() {Thread t =
new Thread(new
ThreadStart(P));Console.W
rite("start");t.Start();t.Join();
// si sincronizza con la fine del threadConsole.W
riteLine("end");}
}Output
start--------------------end
12
A. Longheu –Tecniche di programmazione avanzata
Gestione dell’Abort
�Abort lancia una eccezione, gestibile dallo stesso thread
using System; using System
.Threading;class Test {static void P() {
try {try {
try {while (true) ;
} catch (ThreadAbortException) { Console.WriteLine("--
inner aborted"); }} catch (ThreadAbortException) { Console.W
riteLine("--outer aborted");
}} finally { Console.WriteLine("--
finally"); }}
static void Main(string[] arg) {
Thread t = new
Thread(new ThreadStart(P));
t.Start(); Thread.Sleep(1);t.Abort(); t.Join(); Console.W
riteLine("done");}
}Output--inner aborted
--outer aborted
--finally
done
13
A. Longheu –Tecniche di programmazione avanzata
Begin/end Invoke
�BeginInvoke�System
.AsyncCallback èun delegato da chiam
are quando il m
etodo termina
�Object è
disponibile quando il metodo term
ina
�EndInvoke�Blocking
�Polling
�Waiting
�callbacks
14
A. Longheu –Tecniche di programmazione avanzata
Creazione di un thread
�La creazione di un nuovo oggetto T
hreaddeterm
ina la generazione di un nuovo
thread gestito.
La classe
Thread
dispone di
costruttori che
accettano un
delegato ThreadStart
o Param
eterizedThreadStart. Il
delegato esegue
il wrapping del m
etodo richiamato dal nuovo thread
quando viene eseguita una chiamata al m
etodo Start. �Il
metodo
Startrestituisce
immediatam
ente un
valore, spesso
prima
dell'avvio effettivo del nuovo thread. Èpossibile utilizzare le proprietà
ThreadStatee IsAlive
per determinare lo stato del thread, m
a queste proprietà
non dovrebbero mai essere utilizzate per la sincronizzazione.
�È
possibile passare
parametri
ad un
thread: il
delegato ParameterizedThreadStart
offre una tecnica semplice per passare un
oggetto contenente dati a un thread quando viene chiamato l'overload del
metodo
System.Threading.Thread.Start(System
.Object).
Un'alternativa
(modo 2) consiste nell'incapsulare la routine del thread e i dati in una
classe di supporto e nell'utilizzare il delegato ThreadStart.
�Nessuno di questi delegati restituisce un valore perché
non èpossibile
definire una
posizione per
la restituzione
dei dati
da una
chiamata
asincrona. Per recuperare i risultati di un metodo di thread, è
possibile utilizzare un m
etodo di callback
15
A. Longheu –Tecniche di programmazione avanzata
�Esem
pio di passaggio parametri ad un thread tramite modo 1:
using System; using System
.Threading;public class W
ork{ public static void M
ain(){Thread new
Thread =
new Thread(new
ParameterizedThreadStart(W
ork.DoW
ork)); // U
se the overload of the Start method w
ith an Object param
eter new
Thread.Start(42);Work w
= new
Work();
newThread =
new Thread(new
ParameterizedThreadStart(w
.DoM
oreWork));
newThread.Start("The answ
er."); }public static void D
oWork(object data) {
Console.WriteLine("Static thread procedure. D
ata='{0}'", data);
}public void D
oMoreW
ork(object data) {Console.W
riteLine("Instance thread procedure. Data=
'{0}'", data); }}�Questa tecnica non è
tuttavia indipendente dai tipi, poichéconsente di passare al
metodo
Thread.Start(O
bject)
qualsiasi oggetto.
Un m
odo più
efficiente per
passare i dati a una routine del thread consiste nell'inserire sia la routine che i cam
pi dati in un oggetto di lavoro (modo 2)
Creazione di un thread
16
A. Longheu –Tecniche di programmazione avanzata
�Esem
pio di passaggio parametri ad un thread tramite modo 2:
using System;
using System.Threading;
public class ThreadWithState {
private string boilerplate; private int value;public ThreadW
ithState(string text, int number)
{ boilerplate = text; value =
number; }
public void ThreadProc() { Console.W
riteLine(boilerplate, value); }} // EN
D support class
public class Example {
public static void Main()
{ // Supply the state information required by the task.
ThreadWithState tw
s = new
ThreadWithState(
"This report displays the number {0}.", 42);
Thread t = new
Thread(new ThreadStart(tw
s.ThreadProc));t.Start();Console.W
riteLine("Main thread does som
e work, then w
aits.");t.Join();Console.W
riteLine("Independent task completed; m
ain thread ends."); }}
Creazione di un thread
17
A. Longheu –Tecniche di programmazione avanzata
�Esem
pio di recupero risultati da un thread: nel
costruttore della
classe contenente i dati e il m
etodo di thread viene accettato anche undelegato che
rappresenta il metodo di callback. Al term
ine del thread, si richiama il delegato
using System; using System
.Threading;public class ThreadW
ithState {private string boilerplate;
private int value;// D
elegate used to execute the callback method w
hen the task is complete.
private ExampleCallback callback;
public ThreadWithState(string t, int n, Exam
pleCallback callbackDelegate)
{ boilerplate = t; value =
n; callback = callbackD
elegate; }public void ThreadProc() { Console.W
riteLine(boilerplate, value);if (callback !=
null) callback(1); }}// EN
D support class
public delegate void ExampleCallback(int lineCount);
public class Example { public static void M
ain() { ThreadW
ithState tws =
new ThreadW
ithState("This displays num
ber {0}.", 42,new Exam
pleCallback(ResultCallback));Thread t =
new Thread(new
ThreadStart(tws.ThreadProc));
t.Start(); Console.WriteLine("M
ain thread does some w
ork, then waits.");
t.Join(); Console.WriteLine(“Indep. task com
pleted; main thread ends."); }
public static void ResultCallback(int lineCount) { Console.W
riteLine("Independent task printed {0} lines.",lineCount); }}
Creazione di un thread
18
A. Longheu –Tecniche di programmazione avanzata
Sospensione thread usando metodi di System
.Threading.Thread:�
Il metodo
Sleep, blocca subito il thread corrente per il num
ero di millisecondi
specificato, cedendo il resto della porzione di tempo a un altro
thread. Un thread
non può chiamare Sleep su un altro.
�Èpossibile interrom
pere un thread in attesa chiamando il m
etodo Interrupt sul
thread bloccato per generare un'eccezione ThreadInterruptedException. Il thread dovrebbe
intercettare l'eccezione
ed eseguire
le operazioni
appropriate per
continuare a funzionare. Se il thread ignora l'eccezione, l'ambiente di esecuzione
la intercetta e interrompe il thread.
�Èinoltre possibile sospendere un thread con S
uspend. Q
uando un thread chiama
Suspend su se stesso, la chiamata bloccherà
il thread fino a quando non verràripreso da un altro thread. Q
uando un thread chiama Suspend su un altro, la
chiamata non è
bloccante e provoca la sospensione dell'altro thread. Se si chiama
il metodo R
esume, un altro thread uscirà
dallo stato di sospensione e riprenderàl'esecuzione.
A differenza
di Sleep,
Suspend non
determina
l'interruzione immediata: il CLR
deve attendere che il thread abbia raggiunto un punto sicuro (un punto nell'esecuzione in corrispondenza del quale è
possibile eseguire le operazioni
di Garbage
Collection). Suspend
e Resum
e non
si basano
sulla cooperazione del thread che viene controllato, sono estrem
amente
intrusivi e possono causare alle applicazioni seri problem
i come i deadlock,
che si verificano ad esem
pio se si sospende un thread che contiene una risorsa richiesta da un altro thread. In .N
ET 2.0, Suspende R
esumesono (infine) obsoleti.
Thread
19
A. Longheu –Tecniche di programmazione avanzata
�Lo scheduling dei thread viene stabilito dalla priorità.
�I dettagli
dello scheduler
utilizzato per
determinare
l'ordine in
cui i thread
vengono eseguiti varia in base al sistema operativo.
�In alcuni, viene sem
pre pianificata per prima l'esecuzione del thread con la
prioritàpiù
alta, tra quelli di cui èpossibile l'esecuzione. Se più
thread con la stessa priorità
sono tutti disponibili, l'utilitàdi pianificazione scorre in ciclo i
thread a quella priorità, assegnando a ciascuno di essi una porzione di tempo
fissa in cui effettuare l'esecuzione (time slicing).
�Se un thread con una priorità
piùalta è
disponibile, non verranno eseguiti i thread a priorità
piùbassa. Q
uando non sono piùdisponibili thread eseguibili
a una
determinata
priorità, si
passa alla
prioritàpiù
bassa successiva
e pianifica l'esecuzione dei thread che hanno quella priorità.
�Se diventa eseguibile un thread a priorità
piùalta, il thread a priorità
piùbassa viene interrotto e viene nuovam
ente consentita l'esecuzione del primo.
�Nel sistem
a operativo possono inoltre essere regolate dinamicam
ente le prioritàdel thread m
an mano che l'interfaccia utente di un'applicazione viene spostata tra
il primo piano e il background. In altri O
S èpossibile usare altri algoritm
i. �
Per la
gestione dei
thread in
.NET
vs Window
s, consultare
http://msdn.m
icrosoft.com/it-it/library/74169f59(VS.80).aspx
Thread
20
A. Longheu –Tecniche di programmazione avanzata
�In .N
ET Framew
ork èdisponibile una serie di prim
itive di sincronizzazione per
controllare le
interazioni dei
thread ed
evitare situazioni
di race
condition. Queste
primitive
possono essere
suddivise approssim
ativamente in tre
categorie: blocco, segnalazione e operazioni
interlocked. È
importante
tenere presente
che la
sincronizzazione dei
thread èun processo di cooperazione. Se anche un solo thread accede
direttamente
alla risorsa
protetta ignorando
un meccanism
o di
sincronizzazione, quest'ultimo non potrà
produrre alcun effetto. �Blocchi
�La
forma
piùsem
plice di
blocco è
costituita dall'istruzione
lock
che controlla l'accesso a un blocco di codice (sezione critica). L'istruzione lock viene im
plementata tram
ite i metodi Enter
e Exitdella classe M
onitore
utilizza try…catch…
finally per garantire il rilascio del blocco. �
In genere, l'utilizzo dell'istruzione lock per la protezione di piccoli blocchi di codice, che non si estendono m
ai oltre un singolo metodo, rappresenta
la tecnica ottimale per utilizzare la classe M
onitor.
Sincronizzazione dei thread
21
A. Longheu –Tecniche di programmazione avanzata
�La
classe Monitor
fornisce funzionalità
aggiuntive che
possono essere utilizzate insieme all'istruzione lock:
�Il m
etodo TryEnterconsente a un thread di cui è
interrotta l'esecuzione,
in attesa
di una
risorsa, di
abbandonare l'attesa dopo che è
trascorso un determinato intervallo.
Restituisce un valore booleano che può essere utilizzato
per rilevare ed evitare potenziali deadlock.�Il
metodo
Wait
viene chiam
ato da
un thread
in una
sezione critica.
Cede il
controllo della
risorsa e
si interrom
pe finchéquest'ultim
a èdisponibile.
�I m
etodi Pulsee PulseAll
consentono a un thread che sta per rilasciare il blocco o per chiam
are Wait di inserire uno
o piùthread nella coda dei thread pronti, affinché
possano acquisire il blocco.
Sincronizzazione dei thread
22
A. Longheu –Tecniche di programmazione avanzata
�Oltre M
onitor, esiste la classe Mutex. D
iversamente dalla
classe Monitor,
un mutex
può essere
locale o
globale. I
mutex globali, noti anche com
e mutex denom
inati, risultano visibili
attraverso il
sistema
operativo e
possono essere
utilizzati per sincronizzare i thread in piùprocessi o dom
ini applicazioni. I m
utex locali derivano da MarshalByR
efObject e
possono essere utilizzati oltre i limiti del dom
inio applicazione. �
Oltre
Monitor
e Mutex,
esistono classi
per il
blocco non
esclusivo: Èspesso utile concedere a un num
ero limitato di
thread l'accesso simultaneo a una risorsa. I
semafori e i
blocchi in lettura/scrittu
ra sono progettati per controllare
questo tipo di accesso alle risorse in pool.
Sincronizzazione dei thread
23
A. Longheu –Tecniche di programmazione avanzata
�Segnalazione
�Il m
odo piùsem
plice per attendere un segnale proveniente da un altro thread consiste nell'eseguire una chiam
ata al metodo
Join,
che determina un'interruzione del thread corrente finché
l'altro non viene com
pletato. �Operazioni Interlocked
�Le
operazioni interlocked
sono sem
plici operazioni
atomiche
eseguite su una posizione della mem
oria dai metodi statici della
classe Interlocked. Q
ueste operazioni comprendono addizioni,
incrementi,
decrementi,
scambi,
scambi
condizionali basati
su confronto e operazioni di lettura per valori a 64 bit su piattaform
e a 32 bit. A
nche se nessuna di queste operazioni consiste in un blocco o
in un
segnale, è
possibile utilizzarle
per costruire
blocchi e
segnali. Essendo
native del
sistema
operativo Window
s, le
operazioni interlocked risultano estremam
ente veloci.
Sincronizzazione dei thread
24
A. Longheu –Tecniche di programmazione avanzata
�Sincronizzazione m
ediante lock�La parola chiave lock contrassegna un blocco di istruzioni com
e sezione critica ottenendo il blocco a
esclusione reciproca
per un
determinato
oggetto.Object thisLock =
new Object();
lock (thisLock) {// Sezione critica}
Sincronizzazione dei thread
25
A. Longheu –Tecniche di programmazione avanzata
Lock: lock(Variable) Statem
ent
class Account {// this class is a m
onitorlong val =
0;public void D
eposit(long x) {lock (this) { val +
= x; }
// solo 1 thread alla volta}
public void Withdraw
(long x) {lock (this) { val -=
x; }}
}
Lock effettuato su qualsiasi oggetto (!th
is)
object semaphore =
new object();
...lock (semaphore) { ... critical region ... }
C# vs Java: non metodi sincronizzati ma attrib
uti
[MethodIm
pl(MethodIm
plOptions.Synchronized)]
public void Deposit(long x) {...}
Sincronizzazione dei thread
26
A. Longheu –Tecniche di programmazione avanzata
�L'istruzione
lock impedisce
a un
thread di
accedere a una sezione critica del codice, mentre
un altro thread ègià
presente. �Se un altro thread tenta di accedere a un codice bloccato,
attenderà(in
stato di
blocco) finché
l'oggetto non verràrilasciato
�La
parola chiave
lock chiam
a il metodo
Enterall'inizio del blocco e il m
etodo Exitalla fine
Sincronizzazione dei thread
27
A. Longheu –Tecniche di programmazione avanzata
�L’istruzione lock(v) è
un modo sem
plice per invocare i metodi
della classe Monitor
Monitor.Enter(v);
try {Statement
} finally {Monitor.Exit(v);
}
�Se un thread è
abortito durante l’esecuzione di una istruzione finally è
eseguito e il monitor è
rilasciato
Sincronizzazione dei thread
28
A. Longheu –Tecniche di programmazione avanzata
Wait &
Pulse
Monitor.W
ait(lockedVar);EQ
UIVALE A
wait() in Java (lockedVar è
this)Monitor.Pulse(lockedVar);
EQUIVALE A
notify() in JavaMonitor.PulseAll(lockedVar); EQ
UIVALE A
notifyAll() in Java
Esempio
Thread AThread B
lock(v) {lock(v) {
......
Monitor.W
ait(v);Monitor.Pulse(v);
......
}}
1.A richiede lock(v) e va avanti (regione critica libera).2.A esegue W
ait, cambia stato in sleep e rilascia il lock lock.
3.B richiede lock(v) e va avanti (regione critica libera)..4.B esegue Pulse and risveglia A
. Se necessarrio avviene un context swicth fra A
e B.5.A richiede il lock che fallisce perchè
B iènella regione critica.
6.B rilascia il lock; A può eseguire.
29
A. Longheu –Tecniche di programmazione avanzata
Wait &
Pulse (2)
�Wait(v) and Pulse(v) possono essere chiam
ati solo in una sequenza di istruzioni controllata dal lock(v).
�Dopo l’esecuzione di Pulse(v) e l’attivazione del thread corrente, altri
thread possono richiedere ed ottenere il lock�
Occorre, quindi, verificare la condizione di controllowhile (condition false) M
onitor.Wait(v);
...make condition true;
Monitor.Pulse(v);
�PulseAll(v)
attiva tutti i threads in attesa di v, m
a ad uno solo èperm
esso di continuare
30
A. Longheu –Tecniche di programmazione avanzata
Sincronizzazione di Thread�
Differenza notify vs notifyall:
�you w
ould use notify() rather than notifyAll() only if you know that
exactly oneother thread is w
aiting for the lock. �
It's inefficient to wake 1000 threads and have 999 of them
blockwhile
one executes. Better to just wake one at a tim
e, usually. Though, if the monitor
object must
be made
public, exposed
to unknow
n code,
unknown threads, then you probably need notifyAll(). Because if you
don't control all the code that could possibly wait() on a given
monitor,
you can't guarantee that notify() will get called again.
�How
ever, if you can't control all code that accesses a given monitor,
you're usually vulnerable to other problems too, like deadlock, so m
aybe it is preferrable synchronizing on private m
onitors for this reason. �
Whether you call notify() or notifyAll() is dependant on the state that
you just changed. If you changed the state, in a manner that can
satisfy one w
aiting thread, then you call notify. If you change the state that can satisfy m
ultiple waiting threads --
like releasing a group of semaphores,
or shutting down the system
(special case) --then you call notifyA
ll().
31
A. Longheu –Tecniche di programmazione avanzata
Produttore e consumatore
class Buffer {const int size =
4;char[] buf =
new char[size];
int head = 0, tail =
0, n = 0;
public void Put(char ch) {lock(this) {
while (n =
= size) M
onitor.Wait(this);
buf[tail] = ch; tail =
(tail + 1) %
size; n++;
Monitor.PulseAll(this);
}}public char Get() {
lock(this) {while (n =
= 0) M
onitor.Wait(this);
char ch = buf[head]; head =
(head + 1) %
size;n--;Monitor.PulseAll(this);
return ch;}}}
Produttore piùveloce
PutPutPutPutGet
PutGet
...
Produttore piùveloce
PutPutPutPutGet
PutGet
...
Consumatore più
velocePutGet
PutGet
...
Consumatore più
velocePutGet
PutGet
...
32
A. Longheu –Tecniche di programmazione avanzata
critical
regio
n
entra
nce
roo
m
G1
G2
G3 3 G
etth
reads arriv
e
at the em
pty
bu
ffer
all Get
thread
s are wak
ed u
p;
the first o
ne en
ters the critical
regio
n, read
s the d
ata and
leaves;
G1
G2
G3
re-entra
nce ro
om th
ey en
ter the critical reg
ion
on
e after the o
ther an
d g
o to
sleep
becau
se the b
uffer is e
mp
ty
G1
G2
G3
wa
iting
roo
m
a Pu
tth
read arriv
es;
it enters th
e critical regio
n,
dep
osits its d
ata and
calls PulseA
ll
G1
G2
G3
P1
the o
thers en
ter the w
aiting
roo
m ag
ain, b
ecause th
e bu
ffer
is emp
ty ag
ain
G2
G3
Produttore e consumatore
33
A. Longheu –Tecniche di programmazione avanzata
�È
possibile usare
un oggetto
Mutex
per condividere
risorse in una applicazione multithread.
�Lo stato di un oggetto M
utex può essere:�signaled, quando nessun thread lo possiede
�non-signaled, altrim
enti�
Solo un thread può acquisire un oggetto Mutex.
�Ad esem
pio due thread possono gestire una risorsa condivisa per m
ezzo dell’oggetto Mutex, chi ne ha il
controllo ha accesso alla risorsa fino a quando non rilascia (esplicitam
ente) l’oggetto
Esempio M
utex
34
A. Longheu –Tecniche di programmazione avanzata
Mutex
�L'utilizzo di lock o m
onitor risulta utile per impedire l'esecuzione sim
ultanea di blocchi di codice sensibili ai thread, m
a questi costrutti non consentono la com
unicazione di un evento tra un thread e l'altro. Per questa funzione sono necessari gli eventi di sincronizzazione, ossia oggetti caratterizzati da uno di due diversi
stati, segnalato
e non
segnalato, che
possono essere
utilizzati per
attivare e sospendere i thread. L'esecuzione di un thread che tenta di attendere un evento già
segnalato continua senza ritardi.�
Due tipi di eventi di sincronizzazione: AutoR
esetEvente M
anualResetEvent. La
differenza èche AutoR
esetEvent passa automaticam
ente dallo stato segnalato allo stato non segnalato ogni volta che attiva un thread. M
anualResetEvent
consente l'attivazione di un numero qualsiasi di thread con lo stato segnalato e
torna allo stato non segnalato solo quando viene chiamato R
eset.�
un evento èun oggetto che può assum
ere 2 stati: signaled e nonsignaled. �
Un evento dispone di un handle che si utilizza con W
aitHandle com
e un mutex.
�Un thread che attende un evento sarà
bloccato finchèun altro thread segnala
l'evento chiamando M
anualResetEvent.Set o AutoR
esetEvent.Set. �
Se si usa ManualR
esetEvent ènecessario usare il suo m
etodo Reset per
riportarlo allo stato nonsignaled. �
Un AutoR
esetEvent riporta automaticam
ente allo stato nonsignaledappena
al thread in attesa ènotificato che l'evento è
diventato signaled
35
A. Longheu –Tecniche di programmazione avanzata
Esempio M
utex
using System;
using System.Threading;
public class MutexSam
ple { static M
utex gM1; static M
utex gM2;
const int ITERS = 100;
static AutoResetEvent Event1 = new
AutoResetEvent(false); static AutoResetEvent Event2 =
new AutoResetEvent(false);
static AutoResetEvent Event3 = new
AutoResetEvent(false); static AutoResetEvent Event4 =
new AutoResetEvent(false);
public static void Main(String[] args) {
gM1 =
new Mutex(true,"M
yMutex");
gM2 =
new Mutex(true);
AutoResetEvent[] evs = new
AutoResetEvent[4]; // Event for t1.
// Associazione degli eventi definiti come AutoResetEvent
evs[0] = Event1; // Event for t1.
evs[1] = Event2; // Event for t2.
evs[2] = Event3; // Event for t3.
evs[3] = Event4; // Event for t4
}
36
A. Longheu –Tecniche di programmazione avanzata
MutexSam
ple tm = new
MutexSam
ple( ); Thread t1 =
new Thread(new
ThreadStart(tm.t1Start));
Thread t2 = new
Thread(new ThreadStart(tm
.t2Start)); Thread t3 =
new Thread(new
ThreadStart(tm.t3Start));
Thread t4 = new
Thread(new ThreadStart(tm
.t4Start));
// Esegue Mutex.W
aitAll(Mutex[] of gM
1 and gM2).
t1.Start( ); // Esegue M
utex.WaitO
ne(Mutex gM
1). t2.Start( ); // Esegue M
utex.WaitAny(M
utex[] of gM1 and gM
2). t3.Start( ); // Esegue M
utex.WaitO
ne(Mutex gM
2). t4.Start( );
Thread.Sleep(2000); Console.WriteLine(" -
Main releases gM
1"); // quando t2 e t3 finiscono inviano il segnalegM
1.ReleaseMutex( );
Thread.Sleep(1000); Console.WriteLine(" -
Main releases gM
2"); // quando t1 e t4 finiscono inviano il segnale gM
2.ReleaseMutex( );
// il programma attende che tutti i thread finiscano.
WaitH
andle.WaitAll(evs);}
Esempio M
utex
37
A. Longheu –Tecniche di programmazione avanzata
public void t1Start( ) { Console.W
riteLine("t1Start started, Mutex.W
aitAll(Mutex[])");
Mutex[] gM
s = new
Mutex[2];
gMs[0] =
gM1;
gMs[1] =
gM2;
// Waits until both gM
1 and gM2 are released.
Mutex.W
aitAll(gMs);
Thread.Sleep(2000); Console.W
riteLine("t1Start finished, Mutex.W
aitAll(Mutex[]) satisfied");
gM1.ReleaseM
utex(); gM2.ReleaseM
utex(); // AutoResetEvent.Set() flagging m
ethod is done. Event1.Set( );
}
Esempio M
utex
38
A. Longheu –Tecniche di programmazione avanzata
public void t2Start( ) { Console.W
riteLine("t2Start started"); // attendo per gM
1. gM
1.WaitO
ne( ); gM
1.ReleaseMutex();
// metodo AutoResetEvent.Set()
Event2.Set( ); } public void t3Start( ) {
Mutex[] gM
s = new
Mutex[2];
gMs[0] =
gM1;
gMs[1] =
gM2;
int index = Mutex.W
aitAny(gMs);
gMs[index].ReleaseM
utex(); // AutoResetEvent.Set() Event3.Set( );
} public void t4Start( ) {gM
2.WaitO
ne( ); gM
2.ReleaseMutex();
// AutoResetEvent.Set() Event4.Set( );
}
Esempio M
utex
39
A. Longheu –Tecniche di programmazione avanzata
ThreadPool
�In
generale l'utilizzo
della classe
ThreadPoolrappresenta
il modo
piùsem
plice per
gestire più
thread per
attivitàrelativam
ente brevi senza bloccare altri thread.�
ThreadPool Fornisce un pool di thread che può essere utilizzato per inviare elem
enti di lavoro, per elaborare operazioni di I/O
asincrone, per
attendere per
conto di
altri thread
e per
elaborare timer.
�Per
ogni processo
èdisponibile
un pool
di thread,
con dim
ensioni predefinite
pari a
25 thread
per processore
disponibile. �
Il numero di thread nel pool di thread può essere m
odificato utilizzando il m
etodo SetMaxThreads.
�Ciascun thread utilizza le dim
ensioni dello stack predefinite e viene eseguito in base alla priorità
predefinita
40
A. Longheu –Tecniche di programmazione avanzata
ThreadPool
�I pool
di thread
vengono spesso
utilizzati nelle
applicazioni server. Ad ogni richiesta in arrivo viene assegnato un thread del pool. In questo m
odo la richiesta può essere elaborata in modo
asincrono, senza
bloccare il
thread prim
ario o
ritardare l'elaborazione delle richieste successive.
�Una volta com
pletata l'attività, il thread del pool torna in una coda di thread in attesa, dove potrà
essere riutilizzato evitando in questo m
odo la necessitàdi creare un nuovo thread per ogni
attività.�
Esiste in genere un numero m
assimo di thread che è
possibile inserire in un pool. Se tutti i thread sono occupati, le altre attività
vengono inserite in coda finchénon potranno essere
eseguite dai thread che si renderanno disponibili.�
Èpossibile im
plementare un pool di thread personalizzato, m
a risulta più
agevole utilizzare quello fornito con .NET Fram
ework
tramite la classe ThreadPool.
41
A. Longheu –Tecniche di programmazione avanzata
�Esistono
diversi scenari
in cui
èpreferibile
creare e
gestire thread
personalizzati anziché
utilizzare quelli
del pool,
in particolare quando: �Ènecessario un thread in prim
o piano.�Ènecessario che un thread abbia una determ
inata priorità.�Sono presenti attività
che bloccano il thread per periodi di tem
po prolungati. Èprevisto un num
ero massim
o di thread del pool e, pertanto, è
possibile che il blocco di numerosi
thread del pool impedisca l'avvio delle attività.
�È
necessario disporre
di un'identità
stabile associata
al thread
oppure utilizzare
un thread
esclusivamente
per un'attività.
ThreadPool
42
A. Longheu –Tecniche di programmazione avanzata
�Il
pool di
thread viene
utilizzato chiam
ando ThreadPool.Q
ueueUserW
orkItem
dal codice
gestito o
CorQueueU
serWorkItem
dal codice non gestito e passando un delegato W
aitCallbackche rappresenta il m
etodo da cui viene eseguita l'attività.
�È
inoltre possibile
accodare elem
enti di
lavoro correlati
a un'operazione
di attesa
utilizzando il
metodo
ThreadPool.RegisterW
aitForSingleObject
e passando un oggetto WaitH
andleche, alla ricezione di un segnale o alla scadenza,
genera una
chiamata
al metodo
rappresentato dal
delegato WaitO
rTimerCallback.
�In
entrambi
i casi,
il pool
di thread
utilizza un
thread in
background per richiamare il m
etodo di callback.
ThreadPool
43
A. Longheu –Tecniche di programmazione avanzata
�Esempio ThreadCollection (1):
using System;
using System.Threading;
public class Example {
public static void Main() { // Q
ueue the task.ThreadPool.Q
ueueUserW
orkItem(new
WaitCallback(ThreadProc));
Console.WriteLine("M
ain thread does some w
ork, then sleeps.");// If you com
ment out the Sleep, the m
ain thread exits before// the thread pool task runs. The thread pool uses background// threads, w
hich do not keep the application running. (This// is a sim
ple example of a race condition.)
Thread.Sleep(1000);Console.W
riteLine("Main thread exits."); }
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo) {
// No state object w
as passed to QueueU
serWorkItem
, so // stateInfo is null.Console.W
riteLine("Hello from
the thread pool.");}} ThreadPool
44
A. Longheu –Tecniche di programmazione avanzata
�Esempio ThreadCollection in cui si forniscono dati al pool (2):
using System; using System
.Threading;// TaskInfo holds state inform
ation for a task of a ThreadPool thread.public class TaskInfo {
// State information for the task.
public string Boilerplate; public int Value;public TaskInfo(string text, int num
ber) {Boilerplate =
text; Value = num
ber; }}public class Exam
ple {public static void M
ain() {TaskInfo ti =
new TaskInfo("This report displays the num
ber {0}.", 42);// Q
ueue the task and data.if (ThreadPool.Q
ueueUserW
orkItem(new
WaitCallback(ThreadProc), ti))
{ Console.WriteLine("M
ain thread does some w
ork, then sleeps.");Thread.Sleep(1000);Console.W
riteLine("Main thread exits."); }
else { Console.WriteLine("U
nable to queue ThreadPool request."); } }static void ThreadProc(O
bject stateInfo) {TaskInfo ti =
(TaskInfo) stateInfo;Console.W
riteLine(ti.Boilerplate, ti.Value); }}
ThreadPool
45
A. Longheu –Tecniche di programmazione avanzata
The following example demonstrates several threading features (3):
�Queuing
a task
for execution
by ThreadPool
threads, with
the RegisterW
aitForSingleObject
method.
�Signaling a task to execute, w
ith AutoResetEvent.
�Handling both tim
e-outs and signals with a W
aitOrTim
erCallbackdelegate.
�Canceling a queued task w
ith RegisteredW
aitHandle.
using System; using System
.Threading;public class TaskInfo {
public RegisteredWaitH
andle Handle =
null;public string O
therInfo = "default";
}public class Example { public static void M
ain(string[] args){
// The main thread uses AutoResetEvent to signal the registered w
ait handle, // w
hich executes the callback method.
AutoResetEvent ev = new
AutoResetEvent(false);TaskInfo ti =
new TaskInfo(); ti.O
therInfo = "First task";
ThreadPool
46
A. Longheu –Tecniche di programmazione avanzata
The following exam
ple demonstrates several threading features.
…// TaskInfo includes wait handle returned by RegisterW
aitForSingleObject.
// This allows the w
ait ends when the object has been signaled once.
ti.Handle =
ThreadPool.RegisterWaitForSingleO
bject(ev, new
WaitO
rTimerCallback(W
aitProc), ti, 1000, false );// The m
ain thread waits three seconds, to dem
onstrate the// tim
e-outs on the queued thread, and then signals.Thread.Sleep(3100);Console.W
riteLine("Main thread signals.");
ev.Set();// The m
ain thread sleeps, which give the callback tim
e to execute. Thread.Sleep(1000);
// If you start a thread yourself, you can wait for it to end
// by calling Thread.Join. This option is not availablewith thread pool
} ThreadPool
47
A. Longheu –Tecniche di programmazione avanzata
The following exam
ple demonstrates several threading features.
…// The callback m
ethod executes when the registered w
ait times out,
// or when the W
aitHandle (in this case AutoResetEvent) is signaled.
// WaitProc unregisters the W
aitHandle the first tim
e the event is signaled.public static void W
aitProc(object state, bool timedO
ut) {// The state object m
ust be cast to the correct type, because the// signature of the W
aitOrTim
erCallback delegate specifies type Object.
TaskInfo ti = (TaskInfo) state;
string cause = "TIM
ED OUT";
if (!timedO
ut) { cause = "SIG
NALED
";// If the callback m
ethod executes because the WaitH
andle is// signaled, stop future execution of the callback m
ethod// by unregistering the W
aitHandle.
if (ti.Handle !=
null) ti.Handle.U
nregister(null); } Console.W
riteLine("WaitProc( {0} ) executes on thread {1}; cause =
{2}.",ti.O
therInfo, Thread.CurrentThread.GetH
ashCode().ToString(), cause); }}
ThreadPool
48
A. Longheu –Tecniche di programmazione avanzata
Qualche dettaglio:
�The
RegisterWaitForSingleObjectmethod queues the specified delegate
to the
thread pool.
A worker
thread will execute the delegate w
hen the specified object is in the signaled state or the tim
e-out interval elapses. �
The RegisterW
aitForSingleObject
method
checks the
current state
of the
specified object's WaitH
andle. If the object's state is unsignaled, the method
registers a wait operation. The w
ait operation is performed by a
thread from
the thread pool. The delegate is executed by a worker thread w
hen the object's state becom
es signaled or the time-out interval elapses.
�Parameters:
�waitObject: The W
aitHandle
to register.�callBack:
The WaitO
rTimerC
allbackdelegate
to call
when
the waitO
bject param
eter is signaled.�State: The object passed to the delegate.
�millisecondsTimeOutInterval: The tim
e-out in milliseconds
�executeOnlyOnce: true to indicate the thread w
ill no longer wait on the
waitO
bject parameter after the delegate has been called; false to indicate the
timer
is reset
every tim
e the
wait
operation com
pletes until
thewait
is unregistered.
�Return Value: R
egisteredWaitH
andlethat encapsulates the native handle.
ThreadPool
49
A. Longheu –Tecniche di programmazione avanzata
�WaitH
andle Class encapsulates operating system-specific objects that
wait for exclusive access to shared resources.
�This class is typically used as a base class for synchronization
objects. Use the static m
ethods of this class to block a thread until oneor m
ore synchronization objects receive a signal.
�Altri esem
pi allegati�
Esercitazione:�
Si realizzi
un program
ma
che lancia
n thread
per cercare
un elem
ento in una matrice di n per n elem
enti. Ognuno dei thread
cerca l'elemento in una delle righe della m
atrice. Non appena un
thread ha trovato l'elemento cercato, rende note agli altri thread le
coordinate dell'elemento e tutti i thread term
inano. �
Risolvere il problem
a dei cinque filosofi�
Realizzare un server che rim
ane in ascolto di richieste dai client (sim
ulare il
tutto in
localhost); ad
ogni richiesta
di un
client, sganciare un thread che ne soddisfi le richieste, m
entre il thread principale torna in ascolto di nuove connessioni
ThreadPool