2010-02-18 9 views
8

Tengo objetos, tienen bloqueos. Quiero probar si están bloqueados sin adquirir un bloqueo. La idea es si TryEnter() ellos tienen que Salir() si es verdadero para verificar solo el bloqueo correctamente.¿Probar un bloqueo sin adquirirlo?

Parece una pregunta realmente básica, ¿cómo se hace?

Respuesta

22

¿Qué información posible puede obtener al saber que el candado fue desbloqueado cuando lo miró? En el momento en que tome una decisión basada en esa información, el bloqueo ya puede estar tomado.

+3

+1, exactamente. Responder esta pregunta sobre el estado pasado del bloqueo solo puede llevarlo a tomar decisiones incorrectas. – JaredPar

+0

Bloqueo cosas para que pueda ver si se están utilizando. p.ej. Por lo tanto, dos subprocesos no destruyen los mismos recursos compartidos. Quiero ver si ese trabajo se está haciendo. Podría hacer lo mismo con un valor IsBeingUsed pero el objeto para el bloqueo ya existe y los bloqueos actúan de la misma manera, con la excepción de poder verificar si están bloqueados sin establecer el valor en bloqueado. – QueueHammer

+3

Al examinar IsBeingUsed, es posible que el subproceso ya haya detenido un trabajo útil, pero es posible que se haya producido un cambio de contexto antes de actualizar IsBeingUsed. –

2

Debido a que la instrucción lock es equivalente a:

System.Threading.Monitor.Enter(x); 
try { 
    ... 
} 
finally { 
    System.Threading.Monitor.Exit(x); 
} 

¿Puede usted acaba de hacer esto?

bool ObjectWasUnlocked(object x) 
{ 
    if(System.Threading.Monitor.TryEnter(x)) 
    { 
     System.Threading.Monitor.Exit(x); 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

Nota que estoy nombrando a esta función "objeto Fue desbloqueado" en lugar de "ObjectIsUnlocked". No hay garantía de que todavía se desbloqueará cuando la función haya regresado.

+0

En esta solución, sin embargo, adquiere el bloqueo que es algo que OP no quería hacer. – JaredPar

+0

@JaredPar: Sí, buen punto. –

3

Me preguntaba lo mismo al tratar de auditar el código de bloqueo correcto. Se me ocurrió un método usando un segundo hilo. Si el bloqueo está disponible para el hilo que llama, pero no está disponible para un segundo hilo, debe ser retenido por el primero.

/// <summary> 
/// Utiltity for checking if a lock has already been acquired. 
/// WARNING: This test isn't actually thread-safe, 
/// it's only really useful for unit tests 
/// </summary> 
private static bool ObjectIsAlreadyLockedByThisThread(object lockObject) 
{ 
    if (!Monitor.TryEnter(lockObject)) 
    { 
     // another thread has the lock 
     return false; 
    } 

    Monitor.Exit(lockObject); 

    bool? LockAvailable = null; 

    var T = new Thread(() => 
    { 
     if (Monitor.TryEnter(lockObject)) 
     { 
      LockAvailable = true; 
      Monitor.Exit(lockObject); 
     } 
     else 
     { 
      LockAvailable = false; 
     } 
    }); 

    T.Start(); 

    T.Join(); 

    return !LockAvailable.Value; 
} 

// Tests: 
public static void TestLockedByThisThread() 
{ 
    object MyLock = new object(); 
    lock (MyLock) 
    { 
     bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock); 

     Debug.WriteLine(WasLocked); // prints "True" 
    } 
} 

public static void TestLockedByOtherThread() 
{ 
    object MyLock = new object(); 

    var T = new Thread(() => 
    { 
     lock (MyLock) 
     { 
      Thread.Sleep(TimeSpan.FromSeconds(2)); 
     } 
    }); 

    T.Start(); 
    Thread.Sleep(TimeSpan.FromSeconds(1)); 

    bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock); 

    T.Join(); 

    Debug.WriteLine(WasLocked); // prints "False" 
} 

public static void TestNotLocked() 
{ 
    object MyLock = new object(); 

    bool WasLocked = ObjectIsAlreadyLockedByThisThread(MyLock); 

    Debug.WriteLine(WasLocked); // prints "False" 
} 

No utilizaría esto en el código de producción: hay una condición de carrera que podría explotar. Sin embargo, mis pruebas unitarias son en su mayoría de un solo hilo, así que esto fue útil.

+0

Este código está roto. El primer 'Monitor.Exit (lockObject)' (en el bloque 'finally') se llama incluso si el bloqueo no se pudo adquirir. –

+0

@Sebastian Krysmanski: buen punto, no es necesario por fin. arreglado y limpio – GranBurguesa

Cuestiones relacionadas