INTRO 15.
1 C# Thread
15.1 C# Thread INTRO
GENERALITA'
Così come il meccanismo del multitasking permette di avere più processi (task) in
esecuzione contemporanea (pseudo-parallelismo), così il meccanismo del multithreading
permette di avere singoli processi che contengono più attività indipendenti (thread) in
esecuzione contemporanea.
Quando un programma è scritto per avere due o più parti di codice indipendente, si
parla di programma mulithreaded. Ciascuna parte indipendente è detta thread.Un
thread è quindi una unità di codice eseguibile in modo indipendente.
Le CPU multicore non possono esprimere tutta la loro potenza in assenza di programmi
multithreaded!!
Come sappiamo dalla teoria dei sistemi operativi
• il processo è usato dal sistema operativo per rappresentare le diverse
applicazioni (programmi) che possono essere eseguite.
• Il thread è l'unità alla quale è assegnata la CPU (il round-robin è fatto sui thread
e non sui processi).
• Un processo è un contenitore che comprende, tra le altre cose, i thread:
▫ se il programma, che ha dato origine al processo, non è multithreaded, allora il
processo contiene un solo thread, detto main thread, associato al programma (cioè
all'intero spazio di indirizzamento).
▫ se invece il programma è multithreded, il processo contiene il main thread (associato
al Main del programma) e tutti i thread associati alle parti di codice indipendente presenti
nel programma.
• Ogni thread ha un proprio stack di memoria e condivide, con gli altri
thread, lo heap
▫ La comunicazione tra thread (attraverso variabili condivise allocate nello stack
del main thread) è perciò più semplice rispetto a quella tra processi.
alessandra peroni Pag. 1 di 6
INTRO 15.1 C# Thread
IMPLEMENTAZIONE dei THREAD
Nella piattaforma .NET, il namespace System.Threading fornisce
• gli strumenti per la costruzione di applicazioni multithreaded:
▫ classi Thread, ThreadStart
• l'accesso ad una serie di thread gestiti dal CLR (dal sistema di runtime)
▫ classi Timer, Mutex, Monitor, Semaphore
La classe Thread è usata per costruire l'oggetto che racchiude in sé la parte di codice
indipendente. Una volta costruito l'oggetto Thread, è poi possibile usare i metodi per
far partire il thread, sospenderne l'esecuzione, fermarlo, distruggerlo...
OPERATIVAMENTE...
1. Creare una classe con il metodo che contiene il codice indipendente da
associare al thread
public class CodiceIndipendente{
// Questo è il metodo che contiene il codice da associare al thread
public void Attività()
{
while( true ) //ciclo infinito
{
Console.Write("y");
}
}
}
2. Nella classe Program (con il metodo Main) creare un oggetto di tipo
CodiceIndipendente e un oggetto Thread passando, al costruttore di
quest'ultimo, un oggetto ThreadStart che specifica il metodo da associare
al thread:
public class Program
{
public static int Main()
{
CodiceIndipendente codice = new CodiceIndipendente();
Thread th = new Thread(new ThreadStart(codice.Attività));
.........
}
alessandra peroni Pag. 2 di 6
INTRO 15.1 C# Thread
}
3. Far partire il thread creato: th.Start()
public class Program
{
public static int Main()
{
CodiceIndipendente codice = new CodiceIndipendente();
Thread th = new Thread(new ThreadStart(codice.Attività));
th.Start(); //il thread th è NATO!!!!
.........
}
}
NOTA ThreadStart è una classe delegato. Questa classe permette di instanziare
oggetti ai quali si passa, come parametro, il riferimento (indirizzo) a un metodo.
Questo oggetto può poi essere usato per richiamare il metodo corretto nel runtime,
quando l'indirizzo risulta noto. Un delegato è una classe cui si delega il compito di
“far qualcosa”. In questo caso le si delega il compito di chiamare il metodo “corpo
del codice del programma-thread”. Di quale thread? Quello che si sta creando th,
che deve eseguire codice.Attività.
Se osserviamo il programma che abbiamo scritto, possiamo notare che, quando ne
richiediamo l'esecuzione, viene generato un processo che contiene due thread: il main
thread e il thread secondario th. Così, il S.O. ha due thread di cui fare lo scheduling
(cioè da assegnare alla CPU o ai core della CPU multicore) . Possiamo poi osservare che:
• Il main thread termina appena dopo aver generato e mandato in esecuzione il thread th.
• th non termina mai.
Ecco una variante curiosa. Il main thread fa anche lui qualcosa. Eseguendo questo
programma, si vede in modo evidente il comportamento preemptive del sistema
operativo.
using System;
using System.Threading;
namespace ThreadZero
{
class Codice
{
public void Attività()
alessandra peroni Pag. 3 di 6
INTRO 15.1 C# Thread
{
for (int i=0 ; i<300 ; i++) //ciclo finito
Console.Write("y");
}
}
}
namespace ThreadZero
{
class Program
{
static void Main(string[] args)
{
Codice codice = new Codice();
Thread t = new Thread(new ThreadStart(codice.Attività));
t.Start(); // running Attività()
// il main thread fa la stessa cosa
for (int i=0 ; i<300 ; i++) //ciclo finito
Console.Write("x");
}
}
}
Ecco un possibile output:
alessandra peroni Pag. 4 di 6
INTRO 15.1 C# Thread
Perchè possibile? Perchè non c'è garanzia di quando un thread viene selezionato dallo
scheduler. Eseguire più volte il programma per verificare l'asserzione appena fatta.
alessandra peroni Pag. 5 di 6
INTRO 15.1 C# Thread
E' possibile far terminare il main thread solo dopo che il thread secondario è
terminato. Per ottenere questo comportamento si usa il metodo Join(), che è un
metodo che blocca il thread chiamante finchè un thread chiamato non è terminato.
Studiamo attentamente il codice del main thread qui sotto riportato.
public class Program
{
public static int Main()
{
CodiceIndipendente codice = new CodiceIndipendente();
Thread th = new Thread(new ThreadStart(codice.Attività));
th.Start(); //system call: il main thread chiede al S.O. di creare th
while(!th.IsAlive); //system call: il main thread aspetta finché th è vivo
Thread.Sleep(10); //system call: il main thread si sospende per (10 millisec), per
permettere a th di far qualcosa
th.Join(); //system call: il main thread aspetta che th sia terminato
Console.WriteLine(“main thread è terminato”);
}
}
NOTA: ricordarsi di mettere using System.Threading;
Per un thread è anche possibile chiedere la terminazione di un altro thread. Nel
codice precedente è ad esempio possibile inserire la seguente istruzione:
th.Abort(); //system call: il main thread chiede al S.O. di terminare th
appena prima di th.Join().
alessandra peroni Pag. 6 di 6