73

I need to try to lock on an object, and if its already locked just continue (after time out, or without it).

The C# lock statement is blocking.

Sam
  • 7,252
  • 16
  • 46
  • 65
gil
  • 2,248
  • 6
  • 25
  • 31

6 Answers6

113

Ed's got the right function for you. Just don't forget to call Monitor.Exit(). You should use a try-finally block to guarantee proper cleanup.

if (Monitor.TryEnter(someObject))
{
    try
    {
        // use object
    }
    finally
    {
        Monitor.Exit(someObject);
    }
}
Derek Park
  • 45,824
  • 15
  • 58
  • 76
  • +1, thanks! Proposed a little change, if you like, just to remind it's (almost) always possible to use advanced constructs/patterns without nesting braces indefinitely. – ceztko Jul 01 '11 at 19:07
  • @ceztko, can you clarify? In general, releasing N resources in, e.g., C# requires N nested try-finally blocks. The using statement helps avoid this only for IDisposables. Skipping the try-finally blocks may result in leaked resources in the event of an exception. For example, the [Monitor](http://msdn.microsoft.com/en-us/library/aa720302(v=VS.71).aspx) documentation clarifies that the finally block is necessary. Strangely, Ed's link neglects this. – Derek Park Jul 05 '11 at 21:51
44

I believe that you can use Monitor.TryEnter().

The lock statement just translates to a Monitor.Enter() call and a try catch block.

default
  • 11,485
  • 9
  • 66
  • 102
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 1
    What if I'm dealing with inter-process concurrency? Is there some way of using a Mutex in a similar fashion? – Mihai Todor Mar 26 '14 at 10:02
  • This will not work if you are using a method that is async https://stackoverflow.com/questions/21404144/synchronizationlockexception-on-monitor-exit-when-using-await – Noam Aug 16 '19 at 03:55
19

I had the same problem, I ended up creating a class TryLock that implements IDisposable and then uses the using statement to control the scope of the lock:

public class TryLock : IDisposable
{
    private object locked;

    public bool HasLock { get; private set; }

    public TryLock(object obj)
    {
        if (Monitor.TryEnter(obj))
        {
            HasLock = true;
            locked = obj;
        }
    }

    public void Dispose()
    {
        if (HasLock)
        {
            Monitor.Exit(locked);
            locked = null;
            HasLock = false;
        }
    }
}

And then use the following syntax to lock:

var obj = new object();

using (var tryLock = new TryLock(obj))
{
    if (tryLock.HasLock)
    {
        Console.WriteLine("Lock acquired..");
    }
}
cwills
  • 2,136
  • 23
  • 17
  • 1
    As obj isn't static that code is going to get a lock every time it runs. Assuming it's in a class, nothing is ever going to stop that getting a lock. If instead, you Inherited TryLock and that inherited lock had it's own static lock object then you could rely on that locking anywhere. – Tod Apr 01 '21 at 08:46
  • 1
    @Tod the `obj` instance in the second code snippet above is just an example of how to use the TryLock class to lock on any object. Obviously there is no point locking on an object that isn't shared between threads in some way (i.e. a static instance or singleton) - so you're correct, we wouldn't typically initialise a local variable object instance and lock on it. – cwills Apr 08 '21 at 03:40
4

Consider using AutoResetEvent and its method WaitOne with a timeout input.

static AutoResetEvent autoEvent = new AutoResetEvent(true);
if(autoEvent.WaitOne(0))
{
    //start critical section
    Console.WriteLine("no other thread here, do your job");
    Thread.Sleep(5000);
    //end critical section
    autoEvent.Set();
}
else
{
    Console.WriteLine("A thread working already at this time.");
}

See https://msdn.microsoft.com/en-us/library/cc189907(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx and https://msdn.microsoft.com/en-us/library/cc190477(v=vs.110).aspx

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
David Burg
  • 1,064
  • 12
  • 14
  • If millisecondsTimeout is zero, the method does not block. It tests the state of the wait handle and returns immediately. This one works for me and is probably the best answer. I'm editing to give a ready solution. – vezenkov Jun 11 '18 at 10:10
3

You'll probably find this out for yourself now that the others have pointed you in the right direction, but TryEnter can also take a timeout parameter.

Jeff Richter's "CLR Via C#" is an excellent book on details of CLR innards if you're getting into more complicated stuff.

Will Dean
  • 39,055
  • 11
  • 90
  • 118
1

Based on Dereks answer a little helper method:

private bool TryExecuteLocked(object lockObject, Action action)
{
    if (!Monitor.TryEnter(lockObject))
        return false;

    try
    {
        action();
    }
    finally
    {
        Monitor.Exit(lockObject);
    }

    return true;
}

Usage:

private object _myLockObject;
private void Usage()
{
    if (TryExecuteLocked(_myLockObject, ()=> DoCoolStuff()))
    {
        Console.WriteLine("Hurray!");
    }
}
Athanviel
  • 199
  • 1
  • 3
  • 15