Reputation: 148514
I saw jeffery richter video ( click to see exact line) and he says :
It is always better to use Monitor.Enter and Monitor.Lock over Event-wait-handles or Semaphore etc because they (monitor.X) use kernel object , but they only use them if there's contention. and if there's no contention , they dont use those objects.
I maybe missing something here but when I do :
lock(myObj)
{
...
}
I presume that there might be more than one thread who wants to get into the critical section.
So , according to the info above , if there no contention , the lock wont be used ? (what if another thread is about to enter 1 ms after?)
Upvotes: 1
Views: 494
Reputation: 941217
Yes, the Monitor class is very nicely optimized in the CLR. It is very cheap if no other thread already owns the monitor. You don't even pay for extra storage, the locking state is stored in a field in Object that every object already has available, part of the object header.
The Monitor.Enter() method avoids entering OS code by first checking if the lock is already owned by the same thread. Which makes it re-entrant, it simply increments the lock count if that's the case. It next tries to acquire the lock by using the equivalent of Interlocked.CompareExchange(), a very cheap primitive on any processor. The x86 version of it is notable for not actually taking a bus lock at all, you can see the code for it in this answer.
If that didn't work then the operating system needs to get involved because now it is important that it can make a thread context switch to wake the thread back up when the lock is released. Windows highly favors picking a thread that was waiting for an OS synchronization object, which ensures it starts running again as quickly as possible. The underlying object is a simple event, a very cheap OS object. It also takes care of fairness, waiting threads are put in a queue and released in first-in, first-out order. I documented the underlying CLR code in this answer.
Upvotes: 3
Reputation: 182743
So , according to the info above , if there no contention , the lock wont be used ? (what if another thread is about to enter 1 ms after?)
Correct. Then there's contention, and the other thread will have to enter the kernel. Also, the thread that has the lock will also have to enter the kernel when it unlocks it.
The operations kind of look like this:
Lock:
Try to atomically set the user-space lock variable from unlock to lock. If we do, stop, we're done.
Increment the user-space contention count.
Set the kernel-space lock to locked.
Try to atomically set the user-space lock variable from unlock to lock. If we do, decrement the user-space contention count and stop, we're done.
Block in the kernel on the kernel lock.
Go to step 3.
Unlock:
Atomically set the user-space lock variable from lock to unlock.
If the user-space contention count is zero, stop, we're done.
Set the kernel lock to unlocked.
Notice how if there's no contention, the lock operation only involves step 1 and the unlock operation only involves steps 1 and 2, all of which take place in user space.
Upvotes: 2
Reputation: 109537
The lock
statement is just syntactic sugar which uses Monitor.Enter
and Monitor.Exit
as its implementation.
The Monitor
functions themselves are implemented using Condition Variables. Their implementation means that they don't need to allocate a kernel object unless there actually is contention for the lock. When that happens, they do have to go ahead and allocate a kernel object.
Even if there is lock contention, they don't immediately allocate a kernel object. Instead, they "spin" (just sit in a tight loop for a short while) in the hope that the lock becomes free. Only if it doesn't become free will it go on to allocate/use a kernel object.
Note that some of the new synchronisation classes, such as ManualResetEventSlim take this approach too. (In general, any synch class with "Slim" at the end does this.)
Also see this thread.
To directly answer your question about lock
: Yes, if there's no contention, or if the contention only lasts a very short while, there will be no transition to kernel mode in order to use a kernel object. Only if the contention lasts more than a short while will a transition to kernel mode occur.
Upvotes: 2