2011-05-12 16 views
21

Quiero bloquear dos objetos al mismo tiempo. ¿Por qué no puedo escribir como ese código?cómo bloquear varios objetos?

bloqueo (obj1, obj2)

¿Debo escribir siempre así?

lock (obj1) 
{ 
    lock (obj2) 
    { 
    } 
} 

Probablemente esto podría ser más simple? probable sería mejor introducir objeto privado especial y usarlo para una cerradura ...

+1

¿Por qué? De alguna manera creo que solo necesitas 1 bloqueo. –

+0

Posible duplicado de [Cómo usar múltiples variables para un alcance de cerradura en C#] (http://stackoverflow.com/questions/2874028/how-to-use-multiple-variables-for-a-lock-scope-in- c-sharp) –

Respuesta

22

Esa es la forma correcta de bloquear múltiples objetos, sí.

Supongo que la razón por la que solo se permite un único argumento para la instrucción de bloqueo es hacer que el orden en que se bloquean sea lo más claro posible.

Tenga en cuenta que debe asegurarse de que los dos bloqueos se tomen en el mismo orden en cualquier lugar de su código, o que tenga un potencial de interbloqueos.

También podría, como sugiere, introducir un único objeto de bloqueo dedicado, pero eso haría su bloqueo más grueso. Todo depende de tus necesidades. Si a veces solo necesita uno de los bloqueos, debe mantenerlos separados (pero asegúrese de conservar el orden de bloqueo, como se mencionó anteriormente).

+0

Su comentario sobre el "mismo orden en todas partes" es oro, estaba pensando en dos transferencias para las mismas dos cuentas A a B y B a A, bloquearía siempre en orden alfabético por ejemplo para que bloquee A y B sin importar cuáles son las cuentas de origen y de destino; de lo contrario, el bloqueo (A) bloqueo (B) y bloqueo (B) bloqueo (A) bloquearían, funciona incluso si realiza operaciones donde solo una cuenta está involucrada como un retiro. Solo pensé en compartir un ejemplo práctico aquí. Me volvió loco por un tiempo. – Alex

13

Si se escribe código como este, es necesario asegurarse de que, siempre que bloquear esos dos objetos, en este orden. De lo contrario, es posible que se encuentre con puntos muertos.

+2

+1. Justo lo que estaba pensando. – Steven

0

En lugar de bloquear los objetos ellos mismos, crea un objeto dedicado llamado PadLock o similar y solo bloquea aquel donde sea necesario.

+0

Solo por curiosidad: cuando bloqueamos el 'PadLock' ¿también bloquea todo su contenido? Entonces, si candado se refiere a una instancia de 'MyClass', ¿puede otro hilo obtener un bloqueo en esa misma instancia ahora? – Jakub

+0

@Jakub: Sí, puede. Bloquear una instancia de 'PadLock' con' lock' no va a bloquear ningún objeto que contenga. –

0

El bloqueo aquí no significa que durante la duración del bloqueo ningún otro código en otro subproceso puede acceder o modificar el objeto. Si bloquea un objeto, cualquier otro hilo puede modificar el objeto al mismo tiempo. Lo que el bloque de código de bloqueo le permite hacer es hacer que el código dentro del bloque de bloqueo sea de entrada única, es decir, solo un hilo puede ejecutar el bloque de código de bloqueo una vez y otros hilos que intenten ejecutar el mismo bloque de código tendrán que esperar hasta que el propietario el hilo se hace con la ejecución del bloque de código. Entonces, básicamente, no es necesario bloquear 2 o más objetos en casos habituales. Bloqueando su propósito es hacer que el bloque de código de entrada única

+0

El bloqueo no evita que otros subprocesos accedan al objeto, pero ** impide ** que otros subprocesos entren en cualquier bloque de código que se bloquee en el mismo objeto, independientemente de si es el mismo bloque de código o no. Es muy común que se necesiten instrucciones de bloqueo anidadas, ya que un bloque de código frecuentemente necesita adquirir el bloqueo para múltiples recursos cuyos bloqueos se gestionan de forma independiente. – reirab

2

La razón por la que tiene que hacerlo como lo ha escrito, es porque usted no puede bloquear dos objetos en el mismo tiempo; Los bloquea uno tras otro (y es muy importante mantener el orden del bloqueo; de lo contrario, podría pasar a interbloqueos), y es mejor ser tan explícito como sea posible con estas cosas.

1

hacer algo como

internal static void DuoEnter(object po1, object po2, int pnTimeOutMs = 1000) 
    { 
     if ((po1 == null) && (po2 == null)) 
      return; 
     int nMaxLoops = 100 * pnTimeOutMs; 
     bool lOneProcessor = Environment.ProcessorCount < 2; 
     for (int nLoops = 0; nLoops < nMaxLoops; nLoops++) 
     { 
      if ((po1 == null) || (po2 == null) || (po1 == po2)) 
      { 
       if (Monitor.TryEnter(po1 ?? po2)) 
        return; 
      } 
      else 
      { 
       if (Monitor.TryEnter(po1)) 
        if (Monitor.TryEnter(po2)) 
         return; 
        else 
         Monitor.Exit(po1); 
      } 
      if (lOneProcessor || (nLoops % 100) == 99) 
       Thread.Sleep(1); // Never use Thread.Sleep(0) 
      else 
       Thread.SpinWait(20); 
     } 
     throw new TimeoutException(
      "Waited more than 1000 mS trying to obtain locks on po1 and po2"); 
    } 

    internal static void DuoExit(object po1, object po2) 
    { 
     if ((po1 == null) && (po2 == null)) 
      return; 
     if (po1 == null || po2 == null || po1 == po2) 
      Monitor.Exit(po2 ?? po1); 
     else 
     { 
      Monitor.Exit(po2); 
      Monitor.Exit(po1); 
     } 
    } 
+1

Una idea interesante, pero se siente como que estoy entrando furtivamente por la ventana hacia un edificio que tiene una puerta de entrada tapiada. Si algo como esto fuera una buena idea, ¿por qué no sería cocido en C# propiamente dicho? – Nathan

+0

Parece mucho más fácil escribir 'lock (obj1) {lock (obj2) {...}}' – reirab

23

Bueno, esta pregunta es demasiado viejo, pero, aquí es una compacta que me di cuenta, tanto los códigos terminarán a las mismas declaraciones compiladas (esto y el de la Descripción pregunta):

lock (obj1) lock (obj2) 
    { 
     // your code 
    } 
1

he encontrado el mismo tipo de problema, y ​​escribió este fragmento que podría ayudarle a cabo, a pesar de que está lejos de ser perfecta:

private void MultiLock(object[] locks, WaitCallback pFunc, int index = 0) 
{ 
    if (index < locks.Count()) 
    { 
     lock (locks[index]) 
     { 
      MultiLock(locks, pFunc, index + 1); 
     } 
    } 
    else 
    { 
     ThreadPool.QueueUserWorkItem(pFunc); 
    } 
} 

Y entonces, justo llamar a este método como este:

public object LockedObject1 = new Object(); 
public object LockedObject2 = new Object(); 

public void MyFunction(object arg) 
{ 
    WaitCallback pFunc = delegate 
    { 
     // Operations on locked objects 
    } 

    MultiLock(new object[] {LockedObject1, LockedObject2}, pFunc); 
} 
Cuestiones relacionadas