2009-10-21 13 views
36

MSDN ha pronunciato la seguente avviso circa la blocco parola chiave in C#:Perché "lock (typeof (MyType))" è un problema?

In generale, evitare di bloccare su un tipo pubblico, o le istanze di fuori del controllo del vostro codice . I costrutti comuni bloccano (questo), blocco (typeof (MyType)), e blocco ("myLock") violano questa linea guida:

* lock (this) is a problem if the instance can be accessed publicly. 
* lock (typeof (MyType)) is a problem if MyType is publicly accessible. 

Eppure dà alcuna motivazione solida per esso. Il blocco (questo) è spiegato here on SO. Mi interessa il caso di blocco (typeof (MyType)). Cosa c'è di pericoloso?

Grazie.

risposta

53

È pericoloso perché qualsiasi cosa può prendere quel blocco, quindi è difficile o impossibile prevenire una situazione di deadlock.

C'era un articolo su questo ("Do not Lock Type Objects!" Un articolo della GUI della Dr.) con alcuni commenti di Rico Mariani. Apparentemente l'articolo non è più direttamente disponibile, ma ci sono "specchi" che galleggiano intorno, incluso a http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects.

Ecco un estratto:

Il problema di fondo è che non si possiede l'oggetto tipo, e non sai chi altro poteva accedervi. In generale, è una pessima idea affidarsi al blocco di un oggetto che non hai creato e non sapere a chi altri potrebbe accedere. Fare così invita a una situazione di stallo. Il modo più sicuro è bloccare solo oggetti privati.

Ma aspetta; è anche peggio di tutto ciò. A quanto pare, gli oggetti di tipo sono talvolta condivisi tra domini applicativi (ma non tra processi) nelle versioni correnti di .NET runtime. (Questo è generalmente accettabile poiché sono immutabili.) Ciò significa che è possibile che ANOTHER APPLICATION venga eseguito anche in un dominio di applicazione diverso (ma nello stesso processo) per bloccare l'applicazione bloccando un oggetto di tipo che si desidera bloccare e mai rilasciarlo. E sarebbe facile accedere a quell'oggetto di tipo perché l'oggetto ha un nome: il nome completo del tipo! Ricorda i blocchi di blocco/SyncLock (è una parola educata per gli hang) fino a quando non è possibile ottenere un lock. Ovviamente è davvero brutto fare affidamento su un blocco che un altro programma o componente può bloccare e causare il deadlock.

+0

Anche se Jon Skeet è stato il primo, la tua risposta è stata più informativa e ha chiarito la confusione che avevo sul modo in cui funziona la dichiarazione di blocco, quindi ottieni una stella d'oro ... erm ... Voglio dire una casella di controllo verde :) Grazie . –

+1

Grazie al cielo per gli Stati Uniti Handicapper General e il 211 °, 212 ° e 213 ° emendamenti che richiedono l'Harrison Bergeron-like Jon Skeet di usare una tastiera ricoperta di melassa per dare a tutti noi una possibilità di combattere. –

+3

buona risposta. un modello comune che usiamo nel codice framework è di avere un campo istanza privato solo per il blocco: oggetto privato thisLock = new object(); per blocchi statici è possibile utilizzare un campo statico privato: oggetto statico privato staticLock = new object(); – alexdej

25

È lo stesso problema di lock(this) - stai bloccando un riferimento a cui ha accesso altro codice, quindi potrebbe bloccarlo anche su di esso.

Se si dispone di due pezzi indipendenti di codice di blocco sullo stesso riferimento, senza intenzione di escludere l'altro, quindi nella migliore delle ipotesi si potrebbe perdere un po 'di prestazioni a causa di una mancanza di concorrenza - e nel peggiore dei casi potresti introdurre un deadlock.

+2

Hmm ... penso Ho una maggiore incomprensione della parola chiave lock. Dalla tua descrizione sembra che io abbia 2 blocchi in parti del mio programma completamente indipendenti, che si bloccano sullo stesso tipo. Se un blocco è preso da un filo, nessun filo può entrare sia questo che l'altro? –

+3

Esattamente. Stanno usando lo stesso blocco, anche se sono pezzi di codice non correlati. Non va bene. –

+0

D'altra parte, potrebbero esserci delle volte in cui si desidera * consentire * a un codice esterno di sincronizzarsi con il proprio codice. Per questi, probabilmente è meglio pubblicare l'oggetto lock (come tutti i tipi System.Collection.Synchronized tramite la proprietà SyncRoot), ma se è l'unico oggetto mai bloccato dalla classe questo non è in realtà diverso dal lock (Questo). Usare solo oggetti privati ​​per il blocco è possibile solo se * non * vuoi sincronizzarti con codice esterno. –

2

Poiché il risultato di typeof (MyType) (che è un oggetto di tipo Type) è ampiamente accessibile e altro thread può bloccare sullo stesso oggetto, e tenere quella serratura indefinitamente. Quindi la logica interna di MyType ha effettivamente dato un significativo controllo sulla sua logica di sincronizzazione. Questo potrebbe non essere un problema reale se questo è inteso, ma la codifica in modo difensivo/scettico dovrebbe essere il tuo modus operandi.

0

perché il bersaglio di una serratura è solo quello di creare uno spazio per riporre il booleano blocco (Sono bloccato o meno) per altri thread a guardare ....

L'errore comune che l'obiettivo di un il blocco è in realtà in qualche modo essere bloccato è semplicemente sbagliato ... Ciò che è "bloccato" è, .... niente, a meno che nei metodi che possono accedere ad alcuni memoria condivisa in un modo non sicuro, si scrive il codice per guardare questo blocco e non procedere fino a quando non è stato rilasciato ... utilizzando un oggetto Type, poiché la destinazione del blocco è errata perché frammenti di codice in qualsiasi punto dell'intero spazio del processo della soluzione possono accedere a tale oggetto e modificare il blocco di sincronizzazione in cui è memorizzato il blocco booleano. un oggetto localmente ambito consente di assicurare meglio solo quei thread e metodi che possono accedere a o Un pasticcio con la tua memoria condivisa "a rischio" può anche accedere e/o modificare il lucchetto.

0

Viene indicata anche la documentazione nell'argomento "Best practice per il threading gestito". https://msdn.microsoft.com/en-us/library/1c9txz50(v=vs.110).aspx

Dice;

Non utilizzare i tipi come oggetti di blocco. Vale a dire, evitare codice come lock (typeof (X)) in C# o SyncLock (GetType (X)) in Visual Basic o l'uso di Monitor.Enter con oggetti Type per . Per un dato tipo, c'è solo un'istanza di System.Type per dominio dell'applicazione. Se il tipo è attivo, un codice diverso dal proprio può bloccare su di esso i blocchi , provocando lo stato di blocco. Per ulteriori problemi, vedere Reliability Best Practices.

Fare attenzione quando si blocca su istanze, per esempio blocco (questa) in C# o SyncLock (Me) in Visual Basic. Se un altro codice nell'applicazione, esterno al tipo, blocca l'oggetto, le deadlock potrebbero: verificarsi.

0

questo non sarebbe "un problema", se questa forma modificata parallelo dei consigli sono stati seguiti:

In generale, evitare di bloccare su un tipo di pubblico, o le istanze che non hai creare o definire. I costrutti comuni lock (this), lock (typeof (MyType)) violano queste linee guida se non è stato creato l'istanza o dichiarare il tipo ..

Tuttavia, come sopra 'non può essere garantita' per i tipi di pubblico o istanze accessibili attraverso tutto il codice incontrato , l'MSDN e altre fonti sostengono che questi dovrebbero essere evitati per Programmazione difensiva in presenza di un unico problema di runtime (deadlock) potenziale difficile da individuare (). Questo è un buon consiglio dato che la maggior parte programmatori non sono molto buone o diligenti regole ..

..e qualcuno che ha incontrato tale bug in natura sarebbe molto più risoluto circa non permettendo a questa specifica questione si verifichi di nuovo, imponendo le linee guida dichiarate. (Java 1.0/1.1 con il modello di interfaccia utente AWT filettato era particolarmente problematico.)

Il caso di lock ("mylock") è singolarmente speciale in quanto dovrebbe essere evitato a causa di stringhe internato come uno in genere non può "sapere" se stanno violando i consigli precedenti ..

Problemi correlati