2011-08-16 35 views
14

Tengo un método similar a:C# lock en la referencia pasada al método - ¿mala práctica?

public static void DoSomething (string param1, string param2, SomeObject o) 
{ 
    //..... 

    lock(o) 
    { 
     o.Things.Add(param1); 
     o.Update(); 
     // etc.... 
    } 
} 

algunos puntos:

  1. está bloqueando de esta manera una mala práctica?
  2. ¿Debo engancharme con un private static object?
  3. Si es así, ¿por qué?

Respuesta

17

Para minimizar los efectos secundarios, el objeto que está siendo bloqueado en no debe ser el objeto que está siendo manipulado, sino más bien un objeto independiente designado para el bloqueo.

Dependiendo de sus necesidades, hay algunas opciones para manejar este problema:

Variante A: el bloqueo de objetos privada

Elija esta opción si lo que desea es asegurarse de que DoSomething no entra en conflicto con una instancia paralela de DoSomething.

private static readonly object doSomethingLock = new object(); 

public static void DoSomething (string param1, string param2, SomeObject o) 
{ 
    //..... 

    lock(doSomethingLock) 
    { 
     o.Things.Add(param1); 
     o.Update(); 
     // etc.... 
    } 
} 

Variante B: Pase el bloqueo de objetos como parámetro

Elija esta opción si el acceso a o debe ser flujos seguros incluso fuera de DoSomething, es decir, si existe la posibilidad de que alguien escribe un método DoSomethingElse que discurre en paralelo a DoSomething y que no debe interferir con el bloque lock en DoSomething:

public static void DoSomething (string param1, string param2, SomeObject o, object someObjectLock) 
{ 
    //..... 

    lock(someObjectLock) 
    { 
     o.Things.Add(param1); 
     o.Update(); 
     // etc.... 
    } 
} 

Variante C: Crear SyncRoot propiedad

Si usted tiene control sobre la ejecución de SomeObject, podría ser conveniente proporcionar el bloqueo de objetos como una propiedad. De esta manera, se puede aplicar la variante B sin tener que pasar alrededor de un segundo parámetro:

class SomeObject 
{ 
    private readonly object syncRoot = new object(); 

    public object SyncRoot { get { return syncRoot; } } 

    ... 
} 

A continuación, sólo tiene que utilizar lock(o.SyncRoot) en DoSomething. Ese es el patrón que algunas de las clases de BCL usan, por ejemplo, Array.SyncLock, ICollection.SyncRoot.

+2

Creo que 'doSomethingLock' se suele llamar' syncRoot'. – Sung

+0

Creo que veo por qué ahora. No puedo bloquear la referencia pasada al método porque cualquier persona que use ese objeto también podrá bloquearlo. En efecto, es lo mismo que bloquear (esto) pero a una clase diferente. – ghostJago

+0

Los objetos de bloqueo también deben ser de solo lectura. –

0

Aquí es un ejemplo de cómo se debe utilizar el bloqueo:

class Account 
{ 
    decimal balance; 
    private Object thisLock = new Object(); 

    public void Withdraw(decimal amount) 
    { 
     lock (thisLock) 
     { 
      if (amount > balance) 
      { 
       throw new Exception("Insufficient funds"); 
      } 
      balance -= amount; 
     } 
    } 
} 

y esto significa que bloquea un objeto que es una variable privada y sólo se utiliza para bloquear y nada más,

Es posible que wana ver esto:

lock Statement (C# Reference)

y

Thread Synchronization (C# and Visual Basic)

+0

He visto este ejemplo de varias fuentes y entiendo que lo que ha dicho es la mejor práctica. Pero, específicamente, ¿qué hay de malo en bloquear todo el objeto que pasó al método? Después de todo, es un objeto también. – ghostJago

+0

Si habitualmente bloquea el objeto que se pasa, aumenta la probabilidad de crear un interbloqueo. https://en.wikipedia.org/wiki/Deadlock Esencialmente, hay dos subprocesos esperando un bloqueo para un objeto que nunca se puede liberar. –

2

Sólo respondiendo a la tercera pregunta: ¿

Imagínese que este último sobre decide bloquear en otro parámetro de método, tal vez algo como:

public void XXX(object o) 
{ 
    lock(o) 
    { 

    } 
} 

Usted tendrá un tiempo difícil tratando de ver si hay un punto muerto. Deberá verificar que el objeto pasado como parámetro a SomeObject o nunca pase como parámetro al objeto o al mismo tiempo.

Cuestiones relacionadas