2016-03-03 13 views
22

Scuse in anticipo: questa domanda proviene da uno sviluppatore C++ hard-core e non modificato che sta cercando di imparare C# avanzato. Considera quanto segue:È C# 6?. (Elvis op) thread safe? Se é cosi, come?

if (myUserDefinedObject != null) 
{ 
    myUserDefinedObject.ToString(); 
} 

Questo ovviamente non è thread-safe. D'altra parte, ho visto due tutorial che dicono? (Il condizionale operatore Null o 'Elvis Operatore'), per esempio,

myUserDefinedObject?.ToString(); 

IS thread-safe. A meno che il compilatore non avvolga un blocco [mutex?] Attorno ad esso sotto le coperte (brividi), non capisco come possa essere vero. Se questo idioma è infallibile, qualcuno può indicarmi una descrizione tecnica di come è stata realizzata? Se non è thread-safe, qualcuno ha un riferimento che in realtà dice che non lo è?

+3

Ci scusiamo, ma il tuo primo blocco di codice può essere perfettamente thread-safe, a seconda del contesto in cui viene utilizzato e della portata delle variabili coinvolte. –

+2

@KenWhite - Penso che l'idea con il primo blocco sia che un altro thread può impostare la variabile su 'null' dopo il controllo ma prima che' .ToString() 'causi il fallimento del codice. Direi che non è thread-safe. – Enigmativity

+0

@Enigmativity: il poster in particolare diceva che il blocco di codice era ** non thread-safe **, che non è un'istruzione accurata senza conoscere il contesto o l'ambito. Ho fatto notare che l'affermazione era imprecisa: è falso che il codice in quel blocco non sia definitivamente thread-safe. –

risposta

24

Da MSDN (sottolineatura mia):

Un altro uso per l'accesso dell'utente nullo condizione invoca delegati in modo thread-safe con molto meno codice. Il vecchio modo richiede codice come il seguente:

var handler = this.PropertyChanged; 
if (handler != null) 
    handler(…) 

Il nuovo modo è molto più semplice:

PropertyChanged?.Invoke(e) 

Il nuovo modo è thread-safe perché il compilatore genera il codice di valutare solo PropertyChanged una volta, mantenendo il risultato in variabile temporanea.

Quindi non c'è alcun blocco coinvolto qui - la sicurezza del thread viene applicata creando una variabile temporanea locale, che impedisce a un thread diverso di modificare quella variabile tra il controllo Null e qualche altra operazione.

+6

Pertanto, la sicurezza dei thread dipende da ciò che si vuole fare. È possibile ottenere un oggetto ObjectDisposedException se un altro thread chiama Dispose(), ma non si otterrà un'eccezione di puntatore nullo sul sito di chiamata. –

+0

Sarei molto più incline a credere nella documentazione MSDN se qualcuno potesse spiegare come, in C#, tra il momento in cui un indirizzo di memoria viene verificato per non zero e qualche ora futura inconoscibile quando si tenta di accedere ai contenuti di quell'indirizzo, quel contenuto non era stato modificato. Dall'articolo che ho citato sopra, sappiamo che può essere fatto usando * hardware * per 'contrassegnare' quella posizione (usando atomics, memory_modes, ecc.). È questo che sta facendo "il compilatore"? @Hank, potresti anche ottenere una risposta - non solo dall'oggetto che ti aspettavi di essere ancora in quella posizione. ;-) –

+3

Questo non è veramente sicuro come [spiegato qui] (https://msdn.microsoft.com/en-us/magazine/jj883956.aspx) fare riferimento alla sezione "Leggi Introduzione". A volte JIT può eliminare completamente la variabile locale e introdurre un'altra lettura nel campo effettivo. –

28

Desidero chiarire la risposta (corretta) di BJ Myers.

In C# un evento può essere pensato come un campo di tipo delegato - proprio come una proprietà può essere pensata come un campo del tipo di proprietà - e il valore di quel "campo" può essere nullo. Se siete nella spiacevole situazione di dover un gestore di eventi in corso di modifica su un thread mentre un altro thread sta tentando di invocarlo, è possibile ottenere in una situazione in cui:

if (this.SomeEvent != null) 
    this.SomeEvent(...); 

non è threadsafe. Il valore potrebbe essere mutato in modo tale che non sia nullo prima del controllo, nullo dopo il controllo e il programma si blocchi.

Il modo usuale per rendere questo "thread-safe", e io uso il termine consigliato, è quello di copiare il valore su un locale e quindi testare il locale per null. Questo ha il vantaggio di non bloccarsi con un dereferenziamento nullo. Tuttavia, lo sviluppatore intelligente noterà che c'è ancora una gara!La sequenza può essere

gestore di eventi
  • non nullo nella cache sul thread A
  • gestore di eventi impostato nulla sul filo B
  • Stato necessaria per gestore di eventi viene distrutto sul filo B
  • gestore di eventi viene eseguito su thread A e dies orribly

Quindi, in questo senso, questo modello non è "thread safe". Se sei in questa sfortunata posizione , sei responsabile di assicurare che la logica di threading appropriata sia implementata in modo che ciò non avvenga. Puoi farlo come vuoi. Se vuoi i (discutibili) vantaggi di poter chiamare un gestore di eventi su un thread mentre muti l'evento su un altro thread, devi pagare per renderlo sicuro, o gestire bug di race condition.

Personalmente eviterei questa situazione come la peste, ma non sono abbastanza intelligente per scrivere il codice multithread corretto.

Ora, come per la domanda effettiva:

some_expression ?. ToString(); 

è lo stesso di

temp = some_expression 
temp == null ? null : temp.ToString() 

è il secondo codice "threadsafe" secondo te?

+0

Grandi commenti qui - grazie per la tua eccellente spiegazione. "La sicurezza del thread" dipende interamente dal modo in cui i thread stanno usando il codice. –

+0

Quindi ... NON è al sicuro? o è? Abbiamo bisogno di usare un lucchetto per evitare arresti anomali se abbiamo più thread che stanno scherzando? – Narvalex

+4

@Narvalex: Come dovrei saperlo? Spiega * dove stai mettendo il lucchetto * e * in quale razza il lucchetto protegge * e * qual è la tua strategia per evitare i deadlock *, e quindi possiamo avere qualche possibilità di determinare se la tua soluzione è corretta. Ricorda, * la sicurezza del thread è una proprietà di interi programmi *, quindi se vuoi sapere se un programma è protetto da thread, devi fornire un riassunto dell'intero programma. * Ecco perché il threading è difficile *. Stai tenendo un mattone e stai chiedendo "la casa è fatta di mattoni strutturalmente sani?" Quella è una proprietà della casa, non i mattoni. –

Problemi correlati