2011-01-30 9 views
20

Ho scritto un test su ciò che penso dovrebbe essere un caso valido per un deadlock. Sembra che una volta che lo lock sia stato acquisito da un'istanza della classe a, tale istanza non debba più riacquisire lo lock anche se provo esplicitamente a chiamare un altro metodo che dovrebbe essere lock di nuovo.Blocco bloccato e ulteriori tentativi di blocco non bloccare: i blocchi C# sono rientrati?

Qui è la classe:

internal class Tester 
{ 
    private readonly object _sync = new object(); 

    public Tester() { } 

    public void TestLock() 
    { 
     lock (_sync) 
     { 
      for (int i = 0; i < 10; i++) 
      { 
       Deadlock(i); 
      } 
     } 

    } 

    private void Deadlock(int i) 
    { 
     lock (_sync) 
     { 
      Trace.WriteLine(i + " no deadlock!"); 
     } 
    } 
} 

uscita:

0 no stallo!
1 nessun deadlock!
2 nessun deadlock!
3 nessun deadlock!
4 nessun deadlock!
5 nessun deadlock!
6 nessun deadlock!
7 nessun deadlock!
8 nessun deadlock!
9 nessun deadlock!

Avrei pensato che questo avrebbe causato un punto morto ... qualcuno può far luce su questo?

risposta

41

I blocchi in .NET sono rientranti. Solo le acquisizioni da altri thread sono bloccate. Quando lo stesso thread blocca più volte lo stesso oggetto, incrementa semplicemente un contatore e lo decrementa quando viene rilasciato. Quando il contatore raggiunge lo zero, il blocco è effettivamente rilasciato per l'accesso da altri thread.

1

Nel tuo scenario, hai un blocco all'interno di un altro blocco. Quando il codice raggiunge il blocco annidato in "Deadlock", il codice "lock (...)" viene essenzialmente ignorato perché lo ha già acquisito in "TestLock".

Grande fonte per la filettatura: http://www.albahari.com/threading/part2.aspx.

+1

Sono abbastanza a mio agio con il multithreading, ma suppongo che non mi sono mai reso conto che le serrature C# sono rientranti. Grazie per la risposta ... – Kiril

13

Le classi Monitor, Mutex e ReaderWriterLock gestiscono blocchi con affinità di thread. La classe ReaderWriterLockSlim consente di scegliere, ha un costruttore che accetta un valore LockRecursionPolicy. L'utilizzo di LockRecursionPolicy.NoRecursion è un'ottimizzazione, piuttosto grande se il tuo blocco è veramente a grana fine.

La classe Semaphore è una classe di sincronizzazione che non ha alcuna affinità di thread. Questo codice deadlock affidabile:

class Tester { 
    private Semaphore sem = new Semaphore(1, 1); 
    public void TestLock() { 
     sem.WaitOne(); 
     for (int i = 0; i < 10; i++) Deadlock(i); 
     sem.Release(); 
    } 

    private void Deadlock(int i) { 
     if (!sem.WaitOne(100)) Console.WriteLine("deadlock!"); 
     else { 
      sem.Release(); 
      Console.WriteLine("No deadlock!"); 
     } 
    } 
} 

In generale, le classi di sincronizzazione filo affine richiedono due fili e due serrature a un punto morto. Il modello standard è per un thread per acquisire i blocchi A e B, per l'altro per acquisire B e A. L'ordine è importante.

Ci sono meno scenari di deadlock ovvi in ​​giro nella programmazione .NET, indotti da blocchi che non si possono vedere perché sono integrati nel codice framework .NET. Uno molto classico è per BackgroundWorker. È possibile scrivere codice nel thread dell'interfaccia utente che gira sulla proprietà Busy, in attesa del completamento della BGW. Questo si blocca sempre quando BGW ha un gestore di eventi RunWorkerCompleted. Non può essere eseguito finché il thread dell'interfaccia utente non è attivo, la proprietà Busy di BGW non sarà falsa fino a quando il gestore eventi non ha terminato l'esecuzione.

+0

grazie per l'ottima informazione! – Kiril

Problemi correlati