9

I have objects, they get locks. I want to test if they are locked without acquiring a lock. The idea is if I TryEnter() then i have to Exit() if true to only check the lock correctly.

Seems like a really basic question, how is it done?

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
QueueHammer
  • 10,515
  • 12
  • 67
  • 91

4 Answers4

22

What possible information can you get from knowing the lock was unlocked back when you looked at it? By the time you make a decision based on that information, the lock may be already taken.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • 3
    +1, exactly. Answering this question about the past state of the lock can only lead you to making incorrect decisions. – JaredPar Feb 18 '10 at 23:19
  • I lock things so that i can see if they are being used. e.g. So two threads don't smash the same shared resources. I want to see if that work is being done. I could do the same thing with a IsBeingUsed value but the object for the lock already exists and the locks act the same way, with the exception of being able to check if they are locked with out setting the value to locked. – QueueHammer Feb 18 '10 at 23:21
  • 3
    When you examine IsBeingUsed, the thread may already have stopped useful work but a context switch may have happened before IsBeingUsed was updated. – Eric J. Feb 18 '10 at 23:25
  • 1
    @Eric: or worse: IsBeingUsed is 0 and you go ahead and do changes, oblivious to the fact that in the meantime someone did change it to 1. – Remus Rusanu Feb 18 '10 at 23:27
  • 2
    I can see a use for this kind of behaviour. Imagine a situation wherein a lock is acquired for a long period of time, and two threads need to do different things to the same set of objects. If the two collection operations are not order-dependent, then it could be useful to peek at an object first, and skip it to be processed in a later pass if it is in use. – Tullo_x86 Feb 18 '10 at 23:36
  • 6
    @Tullo: But that can be solved better with an zero wait acquire like Monitor.TryEnter. Zero wait aquire means you are willing to grab the lock if is free, but not willing to wait. The OP asked for different semantics, ie. *peek* without acquire. I really can't see any real use for such semantics. – Remus Rusanu Feb 18 '10 at 23:40
  • What if you want to wake-up (i.e. Pulse) a waiting thread only if it is not already running (i.e. doesn't already have the lock)? – mythz Jun 05 '12 at 20:51
  • @mythz: if you're talking about [`Pulse`](http://msdn.microsoft.com/en-us/library/system.threading.monitor.pulse.aspx) that has different semantics: the deterministic owner of the lock pulses the object to rush the waiters, *while having the lock*. – Remus Rusanu Jun 05 '12 at 21:30
  • Right, it needs to **own** the lock in order to `Pulse` waiters, so Monitor.TryEnter can be used to only `Pulse` if no-one already owns the lock. If another thread already owns the lock (in a single consumer), it's already running and doesn't need to be pulsed. – mythz Jun 05 '12 at 22:36
  • There is a perfectly good use for it. Test the state of the lock, if the lock is in-use, don't bother at all ever with the thing you're doing. (Abort permanently if already acquired.) – KatDevsGames Jun 20 '17 at 17:39
  • 1
    @JoshuaPech I think the concensus here was then to use Monitor.TryEnter(or a Zero wait approach). – Tjad Clark Apr 26 '18 at 08:42
3

Because the lock statement is equivalent to:

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

Can you just do this?

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

Note that I'm naming this function "ObjectWasUnlocked" as opposed to "ObjectIsUnlocked". There is no guarantee that it will still be unlocked when the function has returned.

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
3

I was wondering the same thing while trying to audit my code for correct locking. I came up with a method using a second thread. If the lock is available to the calling thread, but unavailable to a second thread, it must be held by the first.

    /// <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"
}

I wouldn't use this in production code - there's a race condition that could blow up. However, my unit tests are mostly single threaded, so this was useful.

GranBurguesa
  • 461
  • 3
  • 13
1

Here is a related question

Checking whether the current thread owns a lock

The conclusion there was 'you can't'

Community
  • 1
  • 1
Phil
  • 2,239
  • 3
  • 25
  • 26