2012-02-07 19 views
43

Mi chiedo se questa costruzione causerà un errore:blocco all'interno di blocco

lock(sync) 
{ 
    // something 
    lock(sync) 
    { 
    //something 
    lock(sync) 
    { 
     //something 
    } 
    } 
} 

ho eseguito questo codice, e sembra bene, ma forse in alcune circostanze può essere generato un errore?

+0

Perché vuoi usare un simile costrutto? – Jehof

+0

Puoi descrivere alcune situazioni in cui dovresti farlo? Sto cercando di capirlo, ma ...? –

+0

@ MatíasFidemraizer Una funzione ricorsiva può bloccare la stessa risorsa. –

risposta

38

lock is a wrapper for Monitor.Enter and Monitor.Exit:

The lock keyword calls Enter at the start of the block and Exit at the end of the block. From the former's documentation:

From the documentation for Monitor.Enter:

It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.

Perché le chiamate a Enter e Exit sono accoppiati, il vostro modello di codice è ben definito il comportamento.

noti, tuttavia, che lock non è garantito un costrutto eccezioni meno:

A ThreadInterruptedException is thrown if Interrupt interrupts a thread that is waiting to enter a lock statement.

+2

Vale la pena notare che i 'lock's interni sono essenzialmente ridondanti: hai * già * il blocco; chiederlo di nuovo non fallirà mai. –

+9

Non vale la pena di notare che, come è comune per i metodi che tengono i blocchi per chiamare metodi che ottengono anche serrature. In alcuni progetti, il secondo blocco avrebbe un deadlock ... Ho lavorato su questi sistemi, ed era necessario che il programma mantenga il proprio conteggio dei riferimenti e solo prendere e rilasciare il blocco al livello più esterno. –

+0

Sono d'accordo con Jim.Ho appena avuto un'istanza in cui in un'applicazione MVC avevo bisogno di un blocco interno. Ho scelto di inserire un'istruzione di blocco negli inserti e negli aggiornamenti con la stessa chiave, quindi ho un'area nel controller che blocco, dove chiudo il report; questo raccoglie i dati per un report di errore, quindi chiama i metodi DAL necessari, alcuni metodi che vengono riutilizzati e possono essere chiamati da altri processi al di fuori del report. Volevo bloccare qualsiasi modifica al database durante la chiusura, quindi il report degli errori è accurato e non si intrufola nulla. Quindi sì, ci sono dei motivi per cui un blocco annidato non è ridondante. – eaglei22

0

Secondo MSDN (vedi here e here) questo è un comportamento ben definito e provoca nessun problema.

12

Per spiegare comportamento perché è ben definito e non sarà mai sicuro:

parte: This answer has better details about how locks actually work

Il blocco avviene a livello Thread, in modo da chiamare una seconda volta sullo stesso filo sarà ridondante . Penserei che non avrebbe alcuna penalizzazione delle prestazioni (anche se ciò dipenderebbe dal modo in cui sono esattamente scritte le parti interne di .Net, quindi non posso garantire che)

Molte volte si dovrebbe avere una funzione pubblica che chiama un altro funzione pubblica nella tua classe, che entrambi hanno bisogno del lucchetto se usati separatamente. Se questo non fosse permesso, il seguente non avrebbe funzionato:

private Dictionary<string, int> database = new Dictionary<string, int>(); 
private object databaseLock = new object(); 
public void AddOrUpdate(string item) 
{ 
    lock (databaseLock) 
    { 
     if (Exists(item)) 
      database.Add(item, 1); 
     else 
      ++database[item]; 
    } 
} 
public bool Exists(string item) 
{ 
    lock (databaseLock) 
    { 
     //... Maybe some pre-processing of the key or item... 
     return database.ContainsKey(item); 
    } 
} 
+0

grazie, stavo cercando di capire se il blocco all'interno del blocco dello stesso oggetto avrebbe funzionato, proprio perché un metodo ne chiama un altro. – mcmillab