C # - Multithreading
UN threadè definito come il percorso di esecuzione di un programma. Ogni thread definisce un flusso di controllo univoco. Se l'applicazione prevede operazioni complicate e dispendiose in termini di tempo, è spesso utile impostare percorsi o thread di esecuzione diversi, con ogni thread che esegue un lavoro particolare.
I thread sono lightweight processes. Un esempio comune di utilizzo del thread è l'implementazione della programmazione concorrente da parte dei sistemi operativi moderni. L'utilizzo di thread consente di risparmiare spreco di ciclo della CPU e aumentare l'efficienza di un'applicazione.
Finora abbiamo scritto i programmi in cui un singolo thread viene eseguito come un singolo processo che è l'istanza in esecuzione dell'applicazione. Tuttavia, in questo modo l'applicazione può eseguire un lavoro alla volta. Per farlo eseguire più di un'attività alla volta, potrebbe essere diviso in thread più piccoli.
Ciclo di vita del filo
Il ciclo di vita di un thread inizia quando viene creato un oggetto della classe System.Threading.Thread e termina quando il thread viene terminato o completa l'esecuzione.
Di seguito sono riportati i vari stati nel ciclo di vita di un thread:
The Unstarted State - È la situazione in cui viene creata l'istanza del thread ma il metodo Start non viene chiamato.
The Ready State - È la situazione in cui il thread è pronto per essere eseguito e in attesa del ciclo della CPU.
The Not Runnable State - Un thread non è eseguibile, quando
- È stato chiamato il metodo del sonno
- Il metodo Wait è stato chiamato
- Bloccato dalle operazioni di I / O
The Dead State - È la situazione in cui il thread completa l'esecuzione o viene interrotto.
Il filo conduttore
In C #, il System.Threading.Threadclass viene utilizzata per lavorare con i thread. Consente la creazione e l'accesso a singoli thread in un'applicazione multithread. Il primo thread da eseguire in un processo è chiamatomain filo.
Quando un programma C # inizia l'esecuzione, il thread principale viene creato automaticamente. I thread creati utilizzando ilThreadsono chiamati thread secondari del thread principale. È possibile accedere a un thread utilizzando ilCurrentThread proprietà della classe Thread.
Il seguente programma mostra l'esecuzione del thread principale:
using System;
using System.Threading;
namespace MultithreadingApplication {
class MainThreadProgram {
static void Main(string[] args) {
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();
}
}
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
This is MainThread
Proprietà e metodi della classe Thread
La tabella seguente mostra alcuni dei più comunemente usati properties del Thread classe -
Sr.No. | Proprietà e descrizione |
---|---|
1 | CurrentContext Ottiene il contesto corrente in cui è in esecuzione il thread. |
2 | CurrentCulture Ottiene o imposta le impostazioni cultura per il thread corrente. |
3 | CurrentPrinciple Ottiene o imposta l'entità corrente del thread (per la sicurezza basata sui ruoli). |
4 | CurrentThread Ottiene il thread attualmente in esecuzione. |
5 | CurrentUICulture Ottiene o imposta le impostazioni cultura correnti usate da Resource Manager per cercare risorse specifiche delle impostazioni cultura in fase di esecuzione. |
6 | ExecutionContext Ottiene un oggetto ExecutionContext che contiene informazioni sui vari contesti del thread corrente. |
7 | IsAlive Ottiene un valore che indica lo stato di esecuzione del thread corrente. |
8 | IsBackground Ottiene o imposta un valore che indica se un thread è o meno un thread in background. |
9 | IsThreadPoolThread Ottiene un valore che indica se un thread appartiene o meno al pool di thread gestiti. |
10 | ManagedThreadId Ottiene un identificatore univoco per il thread gestito corrente. |
11 | Name Ottiene o imposta il nome del thread. |
12 | Priority Ottiene o imposta un valore che indica la priorità di pianificazione di un thread. |
13 | ThreadState Ottiene un valore contenente gli stati del thread corrente. |
La tabella seguente mostra alcuni dei più usati methods del Thread classe -
Sr.No. | Metodo e descrizione |
---|---|
1 | public void Abort() Genera un'eccezione ThreadAbortException nel thread su cui viene richiamato, per iniziare il processo di terminazione del thread. La chiamata a questo metodo di solito termina il thread. |
2 | public static LocalDataStoreSlot AllocateDataSlot() Alloca uno slot di dati senza nome su tutti i thread. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
3 | public static LocalDataStoreSlot AllocateNamedDataSlot(string name) Alloca uno slot di dati con nome su tutti i thread. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
4 | public static void BeginCriticalRegion() Notifica a un host che l'esecuzione sta per entrare in un'area di codice in cui gli effetti di un'interruzione del thread o di un'eccezione non gestita potrebbero compromettere altre attività nel dominio dell'applicazione. |
5 | public static void BeginThreadAffinity() Notifica a un host che il codice gestito sta per eseguire istruzioni che dipendono dall'identità del thread del sistema operativo fisico corrente. |
6 | public static void EndCriticalRegion() Notifica a un host che l'esecuzione sta per entrare in un'area di codice in cui gli effetti di un'interruzione del thread o di un'eccezione non gestita sono limitati all'attività corrente. |
7 | public static void EndThreadAffinity() Notifica a un host che il codice gestito ha terminato l'esecuzione di istruzioni che dipendono dall'identità del thread del sistema operativo fisico corrente. |
8 | public static void FreeNamedDataSlot(string name) Elimina l'associazione tra un nome e uno slot, per tutti i thread nel processo. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
9 | public static Object GetData(LocalDataStoreSlot slot) Recupera il valore dallo slot specificato nel thread corrente, all'interno del dominio corrente del thread corrente. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
10 | public static AppDomain GetDomain() Restituisce il dominio corrente in cui è in esecuzione il thread corrente. |
11 | public static AppDomain GetDomainID() Restituisce un identificatore di dominio dell'applicazione univoco |
12 | public static LocalDataStoreSlot GetNamedDataSlot(string name) Cerca uno slot di dati denominato. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
13 | public void Interrupt() Interrompe un thread che si trova nello stato del thread WaitSleepJoin. |
14 | public void Join() Blocca il thread chiamante fino al termine di un thread, pur continuando a eseguire il pompaggio COM e SendMessage standard. Questo metodo ha diverse forme di overload. |
15 | public static void MemoryBarrier() Sincronizza l'accesso alla memoria come segue: Il processore che esegue il thread corrente non può riordinare le istruzioni in modo tale che l'accesso alla memoria prima della chiamata a MemoryBarrier venga eseguito dopo gli accessi alla memoria che seguono la chiamata a MemoryBarrier. |
16 | public static void ResetAbort() Annulla un'interruzione richiesta per il thread corrente. |
17 | public static void SetData(LocalDataStoreSlot slot, Object data) Imposta i dati nello slot specificato sul thread attualmente in esecuzione, per il dominio corrente di quel thread. Per prestazioni migliori, utilizza invece i campi contrassegnati con l'attributo ThreadStaticAttribute. |
18 | public void Start() Avvia un thread. |
19 | public static void Sleep(int millisecondsTimeout) Rende il thread in pausa per un periodo di tempo. |
20 | public static void SpinWait(int iterations) Fa sì che un thread attenda il numero di volte definito dal parametro iterations |
21 | public static byte VolatileRead(ref byte address) public static double VolatileRead(ref double address) public static int VolatileRead(ref int address) public static Object VolatileRead(ref Object address) Legge il valore di un campo. Il valore è l'ultimo scritto da qualsiasi processore in un computer, indipendentemente dal numero di processori o dallo stato della cache del processore. Questo metodo ha diverse forme di overload. Solo alcuni sono riportati sopra. |
22 | public static void VolatileWrite(ref byte address,byte value) public static void VolatileWrite(ref double address, double value) public static void VolatileWrite(ref int address, int value) public static void VolatileWrite(ref Object address, Object value) Scrive immediatamente un valore in un campo, in modo che il valore sia visibile a tutti i processori nel computer. Questo metodo ha diverse forme di overload. Solo alcuni sono riportati sopra. |
23 | public static bool Yield() Fa sì che il thread chiamante ceda l'esecuzione a un altro thread pronto per essere eseguito sul processore corrente. Il sistema operativo seleziona il thread a cui cedere. |
Creazione di thread
I thread vengono creati estendendo la classe Thread. La classe Thread estesa chiama quindi ilStart() metodo per iniziare l'esecuzione del thread figlio.
Il seguente programma dimostra il concetto:
using System;
using System.Threading;
namespace MultithreadingApplication {
class ThreadCreationProgram {
public static void CallToChildThread() {
Console.WriteLine("Child thread starts");
}
static void Main(string[] args) {
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
In Main: Creating the Child thread
Child thread starts
Gestione dei thread
La classe Thread fornisce vari metodi per la gestione dei thread.
L'esempio seguente mostra l'uso di sleep() metodo per fare una pausa del thread per un periodo di tempo specifico.
using System;
using System.Threading;
namespace MultithreadingApplication {
class ThreadCreationProgram {
public static void CallToChildThread() {
Console.WriteLine("Child thread starts");
// the thread is paused for 5000 milliseconds
int sleepfor = 5000;
Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000);
Thread.Sleep(sleepfor);
Console.WriteLine("Child thread resumes");
}
static void Main(string[] args) {
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes
Distruggere i fili
Il Abort() viene utilizzato per distruggere i thread.
Il runtime interrompe il thread lanciando un file ThreadAbortException. Questa eccezione non può essere catturato, il controllo viene inviato al infine blocco, se presente.
Il seguente programma lo illustra:
using System;
using System.Threading;
namespace MultithreadingApplication {
class ThreadCreationProgram {
public static void CallToChildThread() {
try {
Console.WriteLine("Child thread starts");
// do some work, like counting to 10
for (int counter = 0; counter <= 10; counter++) {
Thread.Sleep(500);
Console.WriteLine(counter);
}
Console.WriteLine("Child Thread Completed");
} catch (ThreadAbortException e) {
Console.WriteLine("Thread Abort Exception");
} finally {
Console.WriteLine("Couldn't catch the Thread Exception");
}
}
static void Main(string[] args) {
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
//stop the main thread for some time
Thread.Sleep(2000);
//now abort the child
Console.WriteLine("In Main: Aborting the Child thread");
childThread.Abort();
Console.ReadKey();
}
}
}
Quando il codice precedente viene compilato ed eseguito, produce il seguente risultato:
In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception