Threading deep dive – Day 6
What are locks?
When there is a shared value that can be accessed by all the threads from your application, data become invalid is possible. Remember we discussed this issue in previous days. So we have to synchronize the access across all threads. How? There come the locks. There is "lock" statement available in C# using which one can lock on some reference type and proceed using the resource. If all other threads from your application also follow this approach [locking on one specific reference type and then continue using the resource], CLR guarantees that would allow only one thread at a time in the lock controlled statements. All other threads would be waiting on the lock statement itself till the thread that gone inside the lock block completes its execution and come out of the block. You may aware that lock internally uses Monitor based locks. There are different kinds of locking mechanisms available with CLR like Monitors, Mutexes, Semaphores etc. You can use these locks based on your requirements like Inter-appdomain, Inter-process , intra-appdomain and intra-process locking.
Below code sample describes the lock usage in C#
object lockOb = new object();
lock (lockOb)
{
// some operations
}
Below is the call stack of the thread that waits on the lock
ntdll!KiFastSystemCallRet
ntdll!NtWaitForMultipleObjects
KERNEL32!WaitForMultipleObjectsEx
mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT
mscorwks!Thread::DoAppropriateAptStateWait
mscorwks!Thread::DoAppropriateWaitWorker
mscorwks!Thread::DoAppropriateWait
mscorwks!CLREvent::WaitEx
mscorwks!CLREvent::Wait
mscorwks!AwareLock::EnterEpilog
mscorwks!AwareLock::Enter
JIT_MonEnterWorker_Portable
I have taken this stack using winDBG. The call stack reveals that the thread could not acquire the lock, waits by calling the function NtWaitForMultipleObjects from ntdll.dll and then transitions to kernel mode by calling KiFastSystemCallRet function from the same dll. Here you may get the question in your mind like why the thread should transition into kernel mode for waiting? We will look later the relationship between thread waiting and kernel mode transitioning.
We use lock statements to synchronize access to resources. Is there any other ways available to synchronize data access? Yes.
We can make use of memory barriers which would force the processor to write all pending writes to RAM.
Lock free data structures where you need not to lock for read / write operations.
Interlocked operations that perform operations atomically.
Tomorrow let us see, what are atomic operations and their internals.