Junior
Cos'è un deadlock?
sobes.tech AI
Risposta dell'AI
Il deadlock è una situazione senza via d'uscita nella programmazione multithread, in cui due o più thread si bloccano a vicenda aspettando risorse occupate da altri thread dello stesso gruppo.
Condizioni per l'insorgere di un deadlock (condizioni di Coffman):
- Esclusione mutua (Mutual Exclusion): Le risorse non possono essere utilizzate contemporaneamente da più thread.
- Ritenzione e attesa (Hold and Wait): I thread che già possiedono una risorsa attendono di ottenere un'altra risorsa occupata da un altro thread.
- Nessuna preemption (No Preemption): Una risorsa non può essere forzatamente sottratta a un thread che la possiede; può essere rilasciata solo dal thread stesso.
- Attesa circolare (Circular Wait): Esiste una catena di due o più thread in cui ogni thread attende una risorsa posseduta dal successivo nella catena (attesa circolare).
Esempio semplice di deadlock:
Due thread, due risorse (A e B).
- Il thread 1 acquisisce la risorsa A.
- Il thread 2 acquisisce la risorsa B.
- Il thread 1 tenta di acquisire B (occupata dal thread 2).
- Il thread 2 tenta di acquisire A (occupata dal thread 1).
Entrambi i thread sono bloccati per sempre.
// Esempio semplice di simulazione di deadlock con lock
object resourceA = new object();
object resourceB = new object();
void Thread1Method()
{
lock (resourceA) // Thread 1 acquisisce A
{
Console.WriteLine("Thread 1 ha acquisito la risorsa A");
Thread.Sleep(100); // Breve pausa per dimostrare la possibilità di cambio di thread
lock (resourceB) // Thread 1 tenta di acquisire B
{
Console.WriteLine("Thread 1 ha acquisito la risorsa B");
}
}
}
void Thread2Method()
{
lock (resourceB) // Thread 2 acquisisce B
{
Console.WriteLine("Thread 2 ha acquisito la risorsa B");
Thread.Sleep(100); // Breve pausa
lock (resourceA) // Thread 2 tenta di acquisire A
{
Console.WriteLine("Thread 2 ha acquisito la risorsa A");
}
}
}
// La creazione e l'avvio dei thread può portare a un deadlock.
// new Thread(Thread1Method).Start();
// new Thread(Thread2Method).Start();
// Eseguire questo codice può, ma non necessariamente, dimostrare un deadlock,
// poiché l'ordine di esecuzione dipende dal scheduler.
// Se il Thread 1 acquisisce A e il Thread 2 B prima che il primo
// possa acquisire B, si verifica un deadlock.
Prevenire i deadlock si ottiene spesso tramite:
- Ordinare l'acquisizione delle risorse (acquisire sempre le risorse nello stesso ordine).
- Utilizzare metodi di acquisizione con timeout (tentare di acquisire una risorsa per un tempo determinato).
- Progettare senza la necessità di mantenere più lock contemporaneamente.
- Utilizzare operazioni atomiche o primitive di sincronizzazione di livello superiore (ad esempio,
Monitor.TryEnter,ReaderWriterLockSlim).
I deadlock sono difficili da debugare poiché dipendono dall'ordine di esecuzione dei thread e spesso si manifestano solo sotto carico. Visual Studio fornisce strumenti per il loro rilevamento e analisi.