When it comes to lock lock, I believe everyone here will not use it, and also know how to use it will not make mistakes, but let them talk about why it can be locked, all say people to group points, probably there are three types of people below the low, middle and high level.

The first group

Making a lock object static enables multiple threads to see the same object, thereby ensuring mutual exclusion and synchronization. Why? I don’t want to pretend that every instance has a synchronized block index, and if I expand it, it won’t hold up, because everybody writes it, and I don’t dare ask, and I don’t want to say, if I put code in, I’m just going to throw it at you.

public class Program { public static object lockMe = new object(); public static void Main(string[] args) { var task1 = Task.Factory.StartNew(() => { lock (lockMe) { //todo } }); var task2 = Task.Factory.StartNew(() => { lock (lockMe) { //todo } }); Task.WaitAll(task1, task2); }}Copy the code

The second class

This type of person may have read a similar biblical work like CLR via C# and be familiar with the concepts.

1. Clarify the layout structure of ‘reference types’ on the heap and the pointer on the stack is to the method table index (type object pointer), as shown in the figure below.

2. Make it clear that when an object is locked, its’ synchronized block index ‘and the’ synchronized block array ‘on the CLR represent an association, and then a graph.

When locking, each thread looks at the pits in the synchronized block array mapped by the synchronized block index of the object to determine whether it can be locked.

Disadvantages: If we must be picky, it is that such people are just listening to others’ stories. In fact, they have no idea whether it is true or not. They just blindly believe in the personality charm of the other party

The third kind of person

This type of person will use resources or contacts to see if, as described by the second type of person, the best tool is Windbg, so I will do it.

1. Additions to the ‘reference type’ layout structure

Now you also know that there are two additional overhead for each object, namely ‘synchronous block index’ + ‘method table index’, which is 4 bytes each on x86 systems and 8 bytes each on X64 systems, since my system is X64 and I’m testing against x64.

2. Case code

With the knowledge supplement above, next I open two tasks, in the task lock operation.

namespace ConsoleApp2 { public class Program { public static void Main(string[] args) { var employee = new Employee(); Console.WriteLine(" Step 1: lock before!! ") ); Console.ReadLine(); Var task1 = task.factory.startNew (() => {lock (Employee) {console. WriteLine(" Step 2: lock1... ") ); Console.ReadLine(); } console. WriteLine(" Step 2: Exit lock1...") ); }); Var task2 = task.factory.startNew (() => {lock (Employee) {console. WriteLine(" Step 2: lock2... ") ); Console.ReadLine(); } console. WriteLine(" Step 2: Exit lock2...") ); }); Task.WaitAll(task1, task2); Console.WriteLine(" Step 3: Lock after all exit!" ); Console.ReadLine(); } } public class Employee { public int a = 1; public int b = 2; }}Copy the code

3. Debug using Windbg

I’m going to do this in three steps: before lock, in lock, after lock, and then get the dump files in these three cases to show the employee object’s synchronized block index and CLR global synchronized block array in real time.

< 1 > before the lock

Run the program and generate the dump file from the task manager.

! threads -> ~0s -> ! Clrstack -l These three commands are used to find the memory address of the local variable Employee on the main thread stack.

0:00 0 >! threads ThreadCount: 2 UnstartedThread: 0 BackgroundThread: 1 PendingThread: 0 DeadThread: 0 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 40b8 00000235222457f0 2a020 Preemptive 0000023523F76D00:0000023523F77FD0 000002352223b0f0 1 MTA 6 2 44c8 00000235222705f0 2b220 Preemptive 0000000000000000:0000000000000000 000002352223b0f0 0 MTA (Finalizer) 0:000> ~0s ntdll! ZwReadFile+0x14: 00007ffa`bd7baa64 c3 ret 0:000> ! clrstack -l OS Thread Id: 0x40b8 (0) Child SP IP Call Site 0000005f721fe748 00007ffabd7baa64 [InlinedCallFrame: 0000005f721fe748] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 0000005f721fe748 00007ffaa5d7b7e8 [InlinedCallFrame: 0000005f721fe748] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 0000005f721fe710 00007ffaa5d7b7e8 *** ERROR: Module load completed but symbols could not be loaded for mscorlib.ni.dll DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 0000005f721fe7f0 00007ffaa65920cc System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef) LOCALS: <no data> <no data> <no data> <no data> <no data> <no data> 0000005f721fe880 00007ffaa6591fd5 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) LOCALS: <no data> <no data> 0000005f721fe8e0 00007ffaa5d470f4 System.IO.StreamReader.ReadBuffer() LOCALS: <no data> <no data> 0000005f721fe930 00007ffaa5d47593 System.IO.StreamReader.ReadLine() LOCALS: <no data> <no data> <no data> <no data> 0000005f721fe990 00007ffaa6738b0d System.IO.TextReader+SyncTextReader.ReadLine()  0000005f721fe9f0 00007ffaa6530d98 System.Console.ReadLine() 0000005f721fea20 00007ffa485d0931 *** WARNING: Unable to verify checksum for ConsoleApp2.exe ConsoleApp2.Program.Main(System.String[]) [C:\dream\Csharp\ConsoleApp1\ConsoleApp2\Program.cs @ 19] LOCALS: 0x0000005f721feaa8 = 0x0000023523f72dc0 0x0000005f721feaa0 = 0x0000000000000000 0x0000005f721fea98 = 0x0000000000000000 0000005f721fecb8 00007ffaa7af6c93 [GCFrame: 0000005f721fecb8]Copy the code

As you can see from LOCALS, the main thread currently has three local variables: Employee, task1, task2, where 0x0000023523f72dc0 is employee.

! dumpobj 0x0000023523f72dc0 -> ! Dumpobj 0000023523F72DD8 Find the memory area of employee on the heap

0:00 0 >! dumpobj 0x0000023523f72dc0 Name: ConsoleApp2.Program+<>c__DisplayClass0_0 MethodTable: 00007ffa484c5af8 EEClass: 00007ffa484c2600 Size: 24(0x18) bytes File: C:\dream\Csharp\ConsoleApp1\ConsoleApp2\bin\x64\Debug\ConsoleApp2.exe Fields: MT Field Offset Type VT Attr Value Name 00007ffa484c5bb8 4000003 8 ConsoleApp2.Employee 0 instance 0000023523f72dd8 employee 0:000> ! dumpobj 0000023523f72dd8 Name: ConsoleApp2.Employee MethodTable: 00007ffa484c5bb8 EEClass: 00007ffa484c2678 Size: 24(0x18) bytes File: C:\dream\Csharp\ConsoleApp1\ConsoleApp2\bin\x64\Debug\ConsoleApp2.exe Fields: MT Field Offset Type VT Attr Value Name 00007ffaa57685a0 4000001 8 System.Int32 1 instance 1 a 00007ffaa57685a0 4000002 c System.Int32 1 instance 2 bCopy the code

Use the view -> Memory menu to view the layout of 0000023523F72DD8 on the heap.


00000235`23f72dc8 d8 2d f7 23 35 02 00 00 00 00 00 00 00 00 00 00  .-.#5...........
00000235`23f72dd8 b8 5b 4c 48 fa 7f 00 00 01 00 00 00 02 00 00 00  .[LH............
Copy the code

From the above, we can see that the first 8 bytes of line 00000235 ’23F72DD8 are synchronized block indexes of Employee. At this time, all of them are 0. Ok, record this status.

< 2 > in the lock

Continue to press Enter on the console, and you can see that Lock1 has acquired the lock.

Using view -> Memory to view the memory index address 0000023523F72DD8, you can see that the original 0 is changed to 0000000007000008, as shown in the following figure.

And then use! Syncblk -all calls out the CLR’s array of global synchronized blocks to see if there is a hole.

6 > 0:00! syncblk -all Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner 1 00000235222af108 0 0 0000000000000000 none 0000023523f77150 System.__ComObject 2 00000235222af158 0 0 0000000000000000 none 0000023523f77170 System.EventHandler`1[[Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs, mscorlib]] 3 00000235222af1a8 0 0 0000000000000000 none 0000023523f771b0 Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs 4 00000235222af1f8 0 0 0000000000000000 none 0000023523f79458 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback 5 00000235222af248 0 0 0000000000000000 none 0000023523f7a158 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback 6 00000235222af298 0 0 0000000000000000 none 0000023523f7a2f8 System.Object 7 00000235222af2e8 3 1 00000235222cb320 56a8 6  0000023523f72dd8 ConsoleApp2.Employee ----------------------------- Total 7 CCW 1 RCW 2 ComClassFactory 0 Free 0Copy the code

See that last line? Consoleapp2.employee has a pit number of 7, which means that 0000000007000008 is associated with the 7, and MonitorHeld=3 means that there is currently a holding thread (+1) and a waiting thread (+2), so this view is validated.

< 3 > after the lock

Proceeding to the console Enter, you can see that both locks are finished. Now what happens to Employee?

Then again, check the memory layout of 0000023523F72DD8.

Curiously, the synchronized block index of the object does not change. Continue to look at the synchronized block array.

0:00 0 >! syncblk -all Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner 1 00000235222af108 0 0 0000000000000000 none 0000023523f77150 System.__ComObject 2 00000235222af158 0 0 0000000000000000 none 0000023523f77170 System.EventHandler`1[[Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs, mscorlib]] 3 00000235222af1a8 0 0 0000000000000000 none 0000023523f771b0 Windows.Foundation.Diagnostics.TracingStatusChangedEventArgs 4 00000235222af1f8 0 0 0000000000000000 none 0000023523f79458 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback 5 00000235222af248 0 0 0000000000000000 none 0000023523f7a158 Microsoft.Win32.UnsafeNativeMethods+ManifestEtw+EtwEnableCallback 6 00000235222af298 0 0 0000000000000000 none 0000023523f7a2f8 System.Object 7 00000235222af2e8 0 0 0000000000000000 none 0000023523f72dd8 ConsoleApp2.Employee 8 00000235222af338 0 0 0000000000000000 none 0000023523f76750 System.IO.TextWriter+SyncTextWriter ----------------------------- Total 8 CCW 1 RCW 2 ComClassFactory 0 Free 0Copy the code

Consoleapp2.employee is currently being held by no thread. The object synchronization block index remains unchanged and the pit in the array may be lazily deleted and initialized by the CLR. Who knows?

conclusion

If I’m right, it’s a significant discovery. If I’m wrong, it’s a limited level 😄😄😄. I’m kidding.

Well, that’s all for this article. I hope it helps you