2009-10-21 25 views
36

MSDN da la siguiente advertencia sobre el bloqueo palabra clave en C#:¿Por qué "lock (typeof (MyType))" es un problema?

En general, evitar el bloqueo en un tipo de público o instancias fuera del control de su código . Las construcciones comunes bloqueo (este), bloqueo (typeof (MyType)), y bloqueo ("myLock") viola este directriz:

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

Sin embargo, no da ninguna razón sólida para ello. El bloqueo (esto) se explica here on SO. Estoy interesado en el caso de bloqueo (typeof (MyType)). ¿Qué es peligroso al respecto?

Gracias.

Respuesta

53

Es peligroso porque cualquier cosa puede tener ese bloqueo por lo que es difícil o imposible evitar una situación de estancamiento.

Solía ​​haber un artículo sobre esto ("No bloquear objetos de tipo!" Un artículo del Dr. GUI) con algunos comentarios de Rico Mariani. Aparentemente el artículo ya no está disponible directamente, pero hay 'espejos' flotando alrededor, incluido en http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects.

He aquí un extracto:

El problema fundamental aquí es que usted no posee el objeto de texto, y usted no sabe quién más podría acceder a él. En general, es una muy mala idea confiar en bloquear un objeto que no creaste y no saber a quién más podría estar accediendo. Hacerlo invita a un punto muerto. La forma más segura es bloquear solo objetos privados.

Pero espere; es incluso peor que todo eso. Resulta que, a veces, los objetos de tipo se comparten entre los dominios de la aplicación (pero no entre los procesos) en las versiones actuales del tiempo de ejecución de .NET. (Esto generalmente está bien ya que son inmutables). Eso significa que es posible que OTRA APLICACIÓN se ejecute incluso en un dominio de aplicación diferente (pero en el mismo proceso) para bloquear su aplicación al obtener un bloqueo en un tipo de objeto que desea bloquear y nunca lo liberaste Y sería fácil acceder a ese tipo de objeto porque el objeto tiene un nombre, ¡el nombre completo del tipo! Recuerda que bloquear/bloques SyncLock (que es una palabra amable para cuelga) hasta que pueda obtener un bloqueo. Obviamente, es bastante malo confiar en un bloqueo que otro programa o componente puede bloquear y provocar un bloqueo.

+0

Aunque Jon Skeet fue el primero, tu respuesta es más informativa y aclara una confusión que tuve sobre la forma en que funciona la instrucción de bloqueo, por lo que obtienes una estrella de oro ... erm ... quiero decir una casilla de verificación verde :) Gracias . –

+1

Gracias a Dios por el Handicapper General de los Estados Unidos y las enmiendas 211, 212 y 213 que requieren que Jon Skeet, como Harrison Bergeron, use un teclado cubierto de melaza para dar al resto de nosotros una oportunidad de luchar. –

+3

buena respuesta. un patrón común que usamos en el código de framework es tener un campo de instancia privada solo para el bloqueo: objeto privado thisLock = new object(); para bloqueos estáticos puede usar un campo estático privado: objeto estático privado staticLock = new object(); – alexdej

25

Es el mismo problema que con lock(this) - está bloqueando una referencia a la que otro código tiene acceso, por lo que también podría estar bloqueada.

Si tiene dos piezas sin relación de código de bloqueo en la misma referencia sin intención para excluir a los demás, a continuación, en el mejor de los casos podría perder un poco de rendimiento debido a la falta de concurrencia - y en el peor de los casos podrías introducir un punto muerto.

+2

Hmm ... creo Tengo una mayor incomprensión de la palabra clave de bloqueo. A partir de su descripción, parece que tengo 2 bloqueos en partes de mi programa que no guardan ninguna relación, que se cierran sobre el mismo tipo. Si un bloqueo se toma por un hilo, ¿ningún hilo puede entrar en este o en el otro? –

+3

Exactamente. Están usando el mismo candado, incluso si son pedazos de código no relacionados. Eso es malo. –

+0

Por otro lado, puede haber ocasiones en las que desee * permitir * que el código externo se sincronice con su propio código. Para estos, probablemente sea mejor publicar el objeto de bloqueo (como todos los System.Collection.Synchronized types lo hacen por medio de la propiedad SyncRoot), pero si es el único objeto bloqueado por su clase, esto no es diferente de lock (esta). Usar solo objetos privados para bloquear solo es realmente posible si * no * desea alguna vez sincronizar con un código externo. –

2

Debido a que el resultado de typeof (MyType) (que es un objeto de tipo Type) es ampliamente accesible y otro hilo puede bloquear en el mismo objeto, y mantenga esa cerradura indefinidamente. Entonces, la lógica interna de MyType efectivamente ha otorgado un control significativo sobre su lógica de sincronización. Esto podría no ser un problema real si esto es así, pero la codificación defensiva/escéptica debería ser su modus operandi .

0

porque el objetivo de una cerradura es sólo para establecer un lugar para almacenar el booleano de bloqueo (¿Estoy bloqueado o no) de otros hilos para mirar ....

La idea errónea de que el objetivo de una el bloqueo está bloqueado de alguna manera, simplemente está mal ... Lo que está "bloqueado" es, .... nada, a menos que en métodos que pueden acceder a alguna memoria compartida de una manera insegura, escriba el código para mirar este bloqueo y no proceda hasta que haya sido liberado ... utilizando un objeto Type, ya que un objetivo de bloqueo es incorrecto porque los fragmentos de código en cualquier punto del proceso completo pueden acceder a ese tipo de objeto y cambiar el bloque de sincronización en el que está almacenado el bloqueo booleano. un objeto de ámbito local le permite garantizar mejor que solo aquellos hilos y métodos que puedan acceder a El problema con la memoria compartida "en riesgo" también puede acceder y/o modificar el bloqueo.

0

Se indica también la documentación bajo el tema "Mejores prácticas administradas para enhebrar". https://msdn.microsoft.com/en-us/library/1c9txz50(v=vs.110).aspx

Dice;

No utilice tipos como objetos de bloqueo. Es decir, evite el código como lock (typeof (X)) en C# o SyncLock (GetType (X)) en Visual Basic, o el uso de Monitor.Enter con objetos Type. Para un tipo determinado, hay solo una instancia de System.Type por dominio de aplicación. Si el tipo que realiza un bloqueo es público, el código que no sea el suyo puede tomar bloqueos en él, lo que lleva a interbloqueos. Para problemas adicionales, vea Reliability Best Practices.

Tenga cuidado al bloqueo en casos, por ejemplo bloqueo (este) en C# o SyncLock (Me) en Visual Basic. Si hay otro código en su aplicación, externo al tipo, tiene un bloqueo en el objeto, bloqueos podría ocurrir.

0

Esto no habría "un problema" si esta forma modificada-paralelo de los consejos fueron seguidos:

En general, evitar el bloqueo en un tipo de público, o de instancias que usted no lo hizo crear o definir. Las construcciones comunes lock (this), lock (typeof (MyType)) violan esta directriz si no crea la instancia o declarar el tipo de ..

Sin embargo, como las anteriores 'no se puede garantizar' para este tipo de públicos o instancias accesibles a través de todo el código encontrado , el MSDN y otras fuentes argumentan que estos deben evitarse para Programación defensiva frente a un potencial singular en tiempo de ejecución difícil de detectar (Interbloqueo) número. Este es un buen consejo dado que la mayoría de los codificadores no son muy buenos o diligentes con las reglas ...

... y alguien que haya encontrado tal error en la naturaleza sería mucho más inflexible sobre y no sobre permitiendo que este problema específico ocurra nuevamente, al imponer las pautas establecidas. (Java 1.0/1.1 con el modelo de UI Threaded AWT fue especialmente problemático.)

El caso de lock ("mylock") es singularmente especial, ya que debe ser evitado debido a la cadena de internar como uno generalmente no puede "saber" si se están violando los consejos anteriores ..

Cuestiones relacionadas