teclas - como hacer que un formulario no se mueva en c#
Diferencia entre bloqueo(casillero) y bloqueo(variable_which_I_am_using) (8)
Estoy usando C # & .NEt 3.5. ¿Cuál es la diferencia entre OptionA y OptionB?
class MyClass
{
private object m_Locker = new object();
private Dicionary<string, object> m_Hash = new Dictionary<string, object>();
public void OptionA()
{
lock(m_Locker){
// Do something with the dictionary
}
}
public void OptionB()
{
lock(m_Hash){
// Do something with the dictionary
}
}
}
Estoy empezando a incursionar en threading (principalmente para crear un caché para una aplicación de subprocesos múltiples, NO utilizando la clase HttpCache, ya que no está adjuntado a un sitio web), y veo la sintaxis de OptionA en muchos de los ejemplos que ver en línea, pero no entiendo qué razón, si la hay, se hace sobre OptionB.
Bloquear el objeto que está utilizando es simplemente una cuestión de conveniencia. Un objeto de bloqueo externo puede simplificar las cosas, y también es necesario si el recurso compartido es privado, como con una colección (en cuyo caso utiliza el objeto ICollection.SyncRoot
).
Bueno, depende de lo que quiere bloquear (se hará seguro).
Normalmente, elegiría la opción B para proporcionar acceso a threadsafe a m_Hash SOLAMENTE. Donde como OptionA, usaría para bloquear el tipo de valor, que no se puede usar con el bloqueo, o tengo un grupo de objetos que necesitan bloquearse al mismo tiempo, pero no sé qué bloquear toda la instancia mediante el lock(this)
Creo que el alcance de la variable que "aprueba" determinará el alcance del bloqueo. es decir, una variable de instancia será con respecto a la instancia de la clase, mientras que una variable estática será para todo el dominio de la aplicación.
Al observar la implementación de las colecciones (utilizando Reflector), el patrón parece seguir que una variable de instancia llamada SyncRoot se declara y usa para todas las operaciones de bloqueo con respecto a la instancia de la colección.
En realidad, no es buena idea bloquear el objeto si está usando sus miembros. Jeffrey Richter escribió en su libro "CLR via C #" que no hay garantía de que una clase de objeto que está utilizando para la sincronización no use lock(this)
en su implementación (es interesante, pero era una forma recomendada de sincronización por Microsoft por algún tiempo ... Luego, descubrieron que era un error), por lo que siempre es una buena idea usar un objeto separado especial para la sincronización. Entonces, como puede ver, OptionB no le dará una garantía de estancamiento: seguridad. Entonces, OptionA es mucho más seguro que OptionB.
Es importante comprender que el bloqueo (m_Hash) NO evita que otro código use el hash. Solo evita que se ejecute otro código que también utiliza m_Hash como su objeto de bloqueo.
Una razón para usar la opción A es que es probable que las clases tengan variables privadas que usará dentro de la instrucción de bloqueo. Es mucho más fácil simplemente usar un objeto que use para bloquear el acceso a todos ellos en lugar de tratar de usar bloqueos de grano más finos para bloquear el acceso solo a los miembros que necesitará. Si intenta usar el método de grano más fino, probablemente tendrá que realizar varios bloqueos en algunas situaciones y luego deberá asegurarse de tomarlos siempre en el mismo orden para evitar interbloqueos.
Otra razón para usar la opción A es porque es posible que la referencia a m_Hash sea accesible fuera de su clase. Tal vez tenga una propiedad pública que le proporcione acceso, o tal vez la declare como protegida y las clases derivadas puedan usarla. En cualquier caso, una vez que el código externo tiene una referencia, es posible que el código externo lo use para un bloqueo. Esto también abre la posibilidad de interbloqueos ya que no tiene forma de controlar o saber en qué orden se realizará el bloqueo.
La opción A es la manera de ir aquí, siempre y cuando en todo su código, cuando acceda al m_hash, use el m_Locker para bloquearlo.
Ahora imagina este caso. Bloquea el objeto. Y ese objeto en una de las funciones que llama tiene un segmento de código de lock(this)
. En este caso, ese es un punto muerto irrecuperable seguro
La opción B usa el objeto a proteger para crear una sección crítica. En algunos casos, esto comunica más claramente la intención. Si se usa de manera consistente, garantiza que solo una sección crítica para el objeto protegido estará activa a la vez:
lock (m_Hash)
{
// Across all threads, I can be in one and only one of these two blocks
// Do something with the dictionary
}
lock (m_Hash)
{
// Across all threads, I can be in one and only one of these two blocks
// Do something with the dictionary
}
La opción A es menos restrictiva. Utiliza un objeto secundario para crear una sección crítica para el objeto a proteger. Si se utilizan múltiples objetos secundarios, es posible tener más de una sección crítica activa para el objeto protegido a la vez.
private object m_LockerA = new object();
private object m_LockerB = new object();
lock (m_LockerA)
{
// It''s possible this block is active in one thread
// while the block below is active in another
// Do something with the dictionary
}
lock (m_LockerB)
{
// It''s possible this block is active in one thread
// while the block above is active in another
// Do something with the dictionary
}
La opción A es equivalente a la Opción B si usa solo un objeto secundario. En cuanto a la lectura del código, la intención de la Opción B es más clara. Si está protegiendo más de un objeto, la opción B no es realmente una opción.
No es lo que está "Bloqueando", es el código que está contenido entre la cerradura {...} eso es importante y está impidiendo que se ejecute.
Si un hilo saca un candado () en cualquier objeto, evita que otros hilos obtengan un bloqueo en el mismo objeto y, por lo tanto, evita que el segundo hilo ejecute el código entre los paréntesis.
Entonces, esa es la razón por la cual la mayoría de las personas simplemente crea un objeto basura para bloquear, evita que otros subprocesos obtengan un bloqueo en ese mismo objeto basura.