2010-06-05 10 views
5

A menudo veo código como el que es shown here, es decir, donde se asigna un objeto y luego se utiliza como un "objeto de bloqueo".Bloquear en un objeto

Me parece que puede usar cualquier objeto para esto, incluido el evento en sí mismo como el objeto de bloqueo. ¿Por qué asignar un nuevo objeto que no hace nada? Según entiendo, llamar a lock() en un objeto no altera realmente el objeto en sí mismo, ni bloquea su uso, simplemente se utiliza como marcador de posición para el anclaje de múltiples instrucciones de bloqueo.

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    object objectLock = new Object(); 

    // Explicit interface implementation required. 
    // Associate IDrawingObject's event with 
    // PreDrawEvent 
    event EventHandler IDrawingObject.OnDraw 
    { 
     add 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent += value; 
      } 
     } 
     remove 
     { 
      lock (objectLock) 
      { 
       PreDrawEvent -= value; 
      } 
     } 
    } 
} 

Así que mi pregunta es, ¿es realmente una buena cosa que hacer?

+0

(ver actualización, por cierto) –

Respuesta

7

incluyendo el evento en sí

No, usted no puede hacer eso. Un "evento" es en realidad solo algunos métodos de acceso. Suponiendo que se refiera al delegado de respaldo, eso sería muy malo: los delegados son inmutables: cada vez que agrega/elimina un suscriptor, obtiene un delegado diferente.

En realidad, el compilador 4.0 ahora hace esto con código de bloqueo usando Interlocked - puede valer la pena seguir este enfoque.

En el ejemplo, el objectLock asegura que todas las personas que llaman (a esa instancia) están encerrando en contra de la misma objeto, que es importante - pero sin la fealdad de bloqueo en this (que es como el compilador de C# utiliza trabajar).

-

Actualización: el ejemplo muestra el código que es necesario antes de C# 4.0, el acceso a un campo como el evento dentro el tipo habló directamente al campo: el campo-como-evento normal de bloqueo no fue respetado Esto fue cambiado en C# 4.0; ahora se puede (en C# 4.0) segura re-escribir esto como:

public class Shape : IDrawingObject, IShape 
{ 
    // Create an event for each interface event 
    event EventHandler PreDrawEvent; 
    event EventHandler PostDrawEvent; 

    event EventHandler IDrawingObject.OnDraw 
    { 
     add { PreDrawEvent += value; } 
     remove { PreDrawEvent -= value; } 
    } 
} 

es seguido Todo el comportamiento correcto.

+0

Entonces, ¿está diciendo que si el bloqueo (PreDrawEvent) daría lugar a que se bloqueara un objeto diferente cada vez que se cambiara el delegado? Uf ... Eso no parece muy ... intuitivo. Siempre he supuesto que los eventos son contenedores, algo así como List <> que contenía delegados ... ¿es una suposición errónea? –

+0

@Mystere Man - peor: un evento no suscrito es 'nulo', por lo que fallará por completo. Tu suposición es incorrecta. –

+1

Hacer suposiciones cuando se trata de bloquear es una muy mala idea. Saber exactamente cómo funciona el bloqueo es primordial. – Rusty

1

Se recomienda bloquear en un campo estático privado, ya que esto garantiza que se bloquearán varios subprocesos que intenten acceder al bloqueo en paralelo. Bloquear en la instancia de la clase misma (lock(this)) o algún campo de instancia podría ser problemático porque si dos hilos llaman al método en dos instancias diferentes del objeto, podrán ingresar simultáneamente la instrucción de bloqueo.

+0

Sí, pero eso no es lo que dije. Me refería a usar el miembro de evento privado como la variable de bloqueo (es decir, PreDrawEvent). También debería tener en cuenta que el ejemplo no usa una variable estática. Además, bloquear esto sería problemático si tuvieras más de un enunciado de bloqueo dentro de la clase. –

+0

Lo mismo: bloquear 'this' o' instance field' podría ser malo, ya que el campo de instancia cambiará dependiendo de la instancia del objeto, mientras que un campo de solo lectura estático nunca cambiará. –

+0

Realmente no veo cuál es el problema. El objetivo es bloquear una instancia de una variable para que varios subprocesos no puedan cambiar esa instancia. ¿Por qué querrías evitar que dos instancias diferentes sean modificadas simultáneamente? –

2

Cualquier miembro del tipo de referencia privada hará el trabajo. Siempre que sea privado y nunca sea reasignado. Que noquea a un objeto delegado fuera de la ejecución, usted definitivamente no quiere ver que un bloqueo no haga su trabajo simplemente porque el código del cliente que no controla asigna un controlador de eventos. Extremadamente difícil de depurar.

El uso de miembros privados que realizan otro trabajo no es algo que se adapte bien. Si descubres que necesitas bloquear otra región de código mientras refactorizas o depuras, necesitarás encontrar otro miembro privado. Que es donde las cosas se pueden agriar rápidamente: puede elegir mismo miembro privado de nuevo. Deadlock llamando a la puerta.

Esto no ocurre si dedica un objeto de bloqueo a un conjunto específico de variables compartidas que deben protegerse. Te permite darle un buen nombre también.

+0

Es por eso que sugirió usar el evento, ya que ese era el "objeto" que estabas manipulando. Sin embargo, si estoy entendiendo los comentarios de Marc correctamente, entonces usar un evento como un objeto de bloqueo es algo muy malo ... –

Cuestiones relacionadas