2011-08-29 19 views
33

C# non consente il blocco su un valore nullo. Suppongo che potrei controllare se il valore è nullo o meno prima di bloccarlo, ma poiché non l'ho bloccato, un altro thread potrebbe venire avanti e rendere il valore nullo! Come posso evitare questa condizione di gara?Perché C# non consente il blocco di un valore nullo?

+4

Perché non basta usare un membro che inizializzato staticamente e * sempre non è nullo * – zerkms

+2

Da quanto ho capito, nulla è sostanzialmente nulla. Come puoi mettere un blocco su nulla? In altre parole, string myString = null dichiara una variabile di tipo string, ma questo è tutto ciò che c'è da fare - non esiste come oggetto perché non ha valore. – Tim

risposta

23

Blocca un valore che non è mai nullo, ad es.

Object _lockOnMe = new Object(); 
Object _iMightBeNull; 
public void DoSomeKungFu() { 
    if (_iMightBeNull == null) { 
     lock (_lockOnMe) { 
      if (_iMightBeNull == null) { 
       _iMightBeNull = ... whatever ...; 
      } 
     } 
    } 
} 

anche essere attenti a evitare questa condizione di gara interessante con bloccaggio doppio controllo: Memory Model Guarantees in Double-checked Locking

+3

Potrebbe essere utile aggiungere in modo univoco all'oggetto di blocco per richiamare l'immutabilità desiderata – cordialgerm

+0

perché il blocco _lockOnMe può impedire ad altri l'accesso a _iMightBeNull? –

+0

-1: il tuo codice è ancora suscettibile alle condizioni della gara a cui ti sei collegato; '_iMightBeNull' deve essere dichiarato come volatile. O, preferibilmente, si dovrebbe usare 'Lazy ' per l'inizializzazione pigra. – Douglas

5

ci sono due problemi qui:

primo luogo, non bloccare su un oggetto null. Non ha senso come in che modo due oggetti, entrambi null, possono essere differenziati?

secondo luogo, per inizializzare sicurezza una variabile in un ambiente multithreading, utilizzare il modello ricontrollato bloccaggio:

if (o == null) { 
    lock (lockObj) { 
     if (o == null) { 
      o = new Object(); 
     } 
    } 
} 

Questo farà sì che un altro filo non è già inizializzato l'oggetto e può essere utilizzato per attuare il Modello Singleton.

48

Non è possibile bloccare su un valore nullo in quanto il CLR non ha posto per fissare lo SyncBlock a, che è ciò che permette al CLR per sincronizzare l'accesso a oggetti arbitrari tramite Monitor.Enter/Exit (che è quello che usa internamente lock)

+0

+1: questa è la risposta più corretta. –

1

Perché C# non consente il blocco di un valore nullo?

Paul's answer è l'unico tecnicamente corretto finora, quindi accetterei quello. È perché i monitor in .NET utilizzano il blocco di sincronizzazione che è collegato a tutti i tipi di riferimento. Se si dispone di una variabile che è null, non si riferisce a nessun oggetto e ciò significa che il monitor non ha accesso a un blocco di sincronizzazione utilizzabile.

Come posso evitare questa condizione di gara?

L'approccio tradizionale consiste nel bloccare un riferimento a un oggetto che non sarà mai null. Se ti trovi in ​​una situazione in cui questo non può essere garantito, classificherei questo approccio non tradizionale. Non c'è molto altro che posso menzionare qui a meno che non descriviate in modo più dettagliato lo scenario particolare che può portare a obiettivi di blocco nullable.

1

La prima parte della tua domanda ha già una risposta, ma vorrei aggiungere qualcosa per la seconda parte della tua domanda.

È più semplice utilizzare un oggetto diverso per eseguire il blocco, specialmente in questa condizione. Questo risolve anche il problema di mantenere gli stati di più oggetti condivisi in una sezione critica, ad es. elenco dei dipendenti e elenco delle foto dei dipendenti.

Inoltre questa tecnica è anche utile quando è necessario acquisire blocco sul tipi primitivi ad esempio int o decimale ecc

A mio parere se usare questa tecnica come tutti gli altri ha suggerito, allora non è necessario eseguire assegno nullo due volte. per esempio.nella risposta accettata Cris ha usato se due volte la condizione che in realtà non fa alcuna differenza perché l'oggetto bloccato è diverso da quello che viene effettivamente modificato, se si sta bloccando su un oggetto diverso allora l'esecuzione del primo controllo null è inutile e lo spreco di CPU .

Vorrei suggerire il seguente codice;

Non riesco a vedere nessuna condizione di gara qui se qualcun altro vede condizioni di gara, si prega di rispondere nei commenti. Come puoi vedere nel codice sopra, risolve 3 problemi in una volta, un controllo null richiesto prima del blocco, in secondo luogo crea una sezione critica senza bloccare due fonti condivise e il terzo blocco più di un oggetto causa deadlock a causa della mancanza di attenzione durante la scrittura codice.

Di seguito viene illustrato come utilizzare i blocchi sui tipi primitivi.

object readonly syncRootIteration = new object(); 

long iterationCount = 0; 
long iterationTimeMs = 0; 

public void IncrementIterationCount(long timeTook) 
{ 
    lock (syncRootIteration) 
    { 
     iterationCount++; 
     iterationTimeMs = timeTook; 
    } 
} 

public long GetIterationAvgTimeMs() 
{ 
    long result = 0; 

    //if read without lock the result might not be accurate 
    lock (syncRootIteration) 
    { 
     if (this.iterationCount > 0) 
     { 
      result = this.iterationTimeMs/this.iterationCount; 
     } 
    } 

    return result; 
} 

Felice filettatura :)

Problemi correlati