Sobes.tech
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):

  1. Esclusione mutua (Mutual Exclusion): Le risorse non possono essere utilizzate contemporaneamente da più thread.
  2. Ritenzione e attesa (Hold and Wait): I thread che già possiedono una risorsa attendono di ottenere un'altra risorsa occupata da un altro thread.
  3. Nessuna preemption (No Preemption): Una risorsa non può essere forzatamente sottratta a un thread che la possiede; può essere rilasciata solo dal thread stesso.
  4. 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.