Monitor vs lock

106,105

Solution 1

Eric Lippert talks about this in his blog: Locks and exceptions do not mix

The equivalent code differs between C# 4.0 and earlier versions.


In C# 4.0 it is:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    { body }
}
finally
{
    if (lockWasTaken) Monitor.Exit(temp);
}

It relies on Monitor.Enter atomically setting the flag when the lock is taken.


And earlier it was:

var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}

This relies on no exception being thrown between Monitor.Enter and the try. I think in debug code this condition was violated because the compiler inserted a NOP between them and thus made thread abortion between those possible.

Solution 2

lock is just shortcut for Monitor.Enter with try + finally and Monitor.Exit. Use lock statement whenever it is enough - if you need something like TryEnter, you will have to use Monitor.

Solution 3

A lock statement is equivalent to:

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

However, keep in mind that Monitor can also Wait() and Pulse(), which are often useful in complex multithreading situations.

Update

However in C# 4 its implemented differently:

bool lockWasTaken = false;
var temp = obj;
try 
{
     Monitor.Enter(temp, ref lockWasTaken); 
     //your code
}
finally 
{ 
     if (lockWasTaken) 
             Monitor.Exit(temp); 
} 

Thanx to CodeInChaos for comments and links

Solution 4

Monitor is more flexible. My favorite use case of using monitor is when you don't want to wait for your turn and just skip:

//already executing? forget it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}

Solution 5

As others have said, lock is "equivalent" to

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

But just out of curiosity, lock will preserve the first reference you pass to it and will not throw if you change it. I know it's not recommended to change the locked object and you don't want to do it.

But again, for the science, this works fine:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        lock (lockObject)
        {
            lockObject += "x";
        }
    }));
Task.WaitAll(tasks.ToArray());

...And this does not:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        Monitor.Enter(lockObject);
        try
        {
            lockObject += "x";
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }));
Task.WaitAll(tasks.ToArray());

Error:

An exception of type 'System.Threading.SynchronizationLockException' occurred in 70783sTUDIES.exe but was not handled in user code

Additional information: Object synchronization method was called from an unsynchronized block of code.

This is because Monitor.Exit(lockObject); will act on lockObject which has changed because strings are immutable, then you're calling it from an unsynchronized block of code.. but anyway. This is just a fun fact.

Share:
106,105

Related videos on Youtube

smartcaveman
Author by

smartcaveman

Does software exist? https://www.codementor.io/smartcaveman

Updated on February 25, 2020

Comments

  • smartcaveman
    smartcaveman about 4 years

    When is it appropriate to use either the Monitor class or the lock keyword for thread safety in C#?

    EDIT: It seems from the answers so far that lock is short hand for a series of calls to the Monitor class. What exactly is the lock call short-hand for? Or more explicitly,

    class LockVsMonitor
    {
        private readonly object LockObject = new object();
        public void DoThreadSafeSomethingWithLock(Action action)
        {
            lock (LockObject)
            {
                action.Invoke();
            }
        }
        public void DoThreadSafeSomethingWithMonitor(Action action)
        {
            // What goes here ?
        }
    }
    

    Update

    Thank you all for your help : I have posted a another question as a follow up to some of the information you all provided. Since you seem to be well versed in this area, I have posted the link: What is wrong with this solution to locking and managing locked exceptions?

  • CodesInChaos
    CodesInChaos over 13 years
    In C#4 the lock statement is implemented differently. blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
  • RobertoBr
    RobertoBr over 13 years
    Look at msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx "In fact, the lock keyword is implemented with the Monitor class. For example"
  • CodesInChaos
    CodesInChaos over 13 years
    As I stated the first example is C#4 and the other is what earlier versions use.
  • kizzx2
    kizzx2 over 13 years
    As a side note, C# via CLR mentions a caveat of the lock keyword: you may often want to do something to restore the corrupt state (if applicable) before releasing the lock. Since the lock keyword does not let us put things in the catch block, we should consider writing the long version try-catch-finally for non trivial routines.
  • CodesInChaos
    CodesInChaos over 13 years
    IMO restoring the shared state is orthogonal to locking/multi-threading. So it should be done with a try-catch/finally inside the lock block.
  • supercat
    supercat almost 11 years
    @kizzx2: I wish .net would make it easier to implement what I would consider the proper locking pattern, which would be to have unexpected exceptions that exit a lock neither release the lock, nor leave it held, but instead invalidate it, such that any pending or future attempts to enter would throw immediate exceptions. That way, code which critically needs the locked resource would fail fast rather than wait indefinitely, and code which would benefit from the resource but doesn't really need it would get on with its work.
  • supercat
    supercat almost 11 years
    @kizzx2: Such a pattern would be especially nice with reader-writer locks. If an exception occurs within code that holds a reader lock, there's no reason to expect that the guarded resource might be damaged, and thus no reason to invalidate it. If an exception occurs within a writer lock and the exception-handling code does not expressly indicate that the guarded object's state has been repaired, that would suggest that the object may be damaged and should be invalidated. IMHO, unexpected exceptions shouldn't crash a program, but should invalidate anything that may be corrupt.
  • eran otzap
    eran otzap almost 11 years
    the underlying implementation of lock uses Monitor but they are not the same thing , consider the methods supplied by monitor which do not exist for lock , and the way you can lock and unlock in separate blocks of code .
  • Arsen Zahray
    Arsen Zahray about 10 years
    Ok. Monitor enter/exit I seem to understand. But than there is a Pulse function, and it seems to be there ta wake some thread. Can anyone explain to me if it's neccessary?
  • CodesInChaos
    CodesInChaos about 10 years
    @ArsenZahray You don't need Pulse for simple locking. It's important in some advanced multi-threading scenarios. I have never used Pulse directly.
  • Yugo Amaryl
    Yugo Amaryl about 5 years
    "This is because Monitor.Exit(lockObject); will act on lockObject". Then lock does nothing with the object? How does lock works?
  • Zhuravlev A.
    Zhuravlev A. almost 5 years
    @YugoAmaryl , I suppose it's because lock statement keeps in mind first passed reference and then use it instead of using changed reference, like: object temp = lockObject; Monitor.Enter(temp); <...locked code...> Monitor.Exit(temp);