C# 中的那些锁,在内核态都是怎么保证同步的?

优质资源分享学习路线指引(点击解锁)知识定位人群定位
实战微信订餐小程序
进阶级
【C# 中的那些锁,在内核态都是怎么保证同步的?】本课程是 flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统 。
量化交易实战
入门级
手把手带你打造一个易扩展、更安全、效率更高的量化交易系统
一:背景 1. 讲故事
其实这个问题是前段时间有位朋友咨询我的,由于问题说的比较泛,不便作答,但想想梳理一下还是能回答一些的,这篇就来聊一聊下面这几个锁 。
Event / phore
用户态层面我就不想说了,网上一搜一大把,我们只聊一聊内核态 。
二:锁玩法介绍 1.
从各种教科书上就可以知道,这个锁非常轻量级,也是各种高手善用的一把锁,为了方便说明,先上一段代码 。
internal class Program{static void Main(string[] args){int location = 1;Interlocked.Increment(ref location);Console.WriteLine(location);Debugger.Break();Interlocked.Increment(ref location);Console.WriteLine(location);Console.ReadLine();}}
这里我们在第二处 .(ref ); 下一个断点,目的是因为此时的函数是 JIT 编译后的方法,接下来我们在中单步调试,会看到如下汇编指令 。
0:000> bp 00007ff8`f6d4298e0:000> gBreakpoint 0 hitConsoleApp2!ConsoleApp2.Program.Main+0x4e:00007ff8`f6d4298e e84550ffffcall00007ff8`f6d379d80:000> t00007ff8`f6d379d8 e9439a7e5ajmpSystem_Private_CoreLib!System.Int32 System.Threading.Interlocked::Increment(System.Int32&)$##6002C3E (00007ff9`51521420)0:000> tSystem_Private_CoreLib!System.Threading.Interlocked.Increment:00007ff9`51521420 b801000000moveax,10:000> tSystem_Private_CoreLib!System.Threading.Interlocked.Increment+0x5:00007ff9`51521425 f00fc101lock xadd dword ptr [rcx],eax ds:00000000`001ceb68=00000002
看到上面的 lock xadd 了吗? 原来类是借助了 CPU 提供的 锁机制 来解决线程同步的, 很显然这种级别的锁相比其他方式的锁性能伤害最小 。
2. ,
大家都知道这种锁的名字叫 事件锁, 其实在上使用场景特别广,就连监视锁() 底层也是用的这种事件锁, 不得不感叹其威力无穷! 而且代码注释中也说了,也就两种状态: 有信号 和 无信号 , 言外之意就是在内核中用了一个 bool 变量来表示,为了能看到这个 bool 值,我们上一个案例 。
internal class Program{static ManualResetEvent mre = new ManualResetEvent(true);static void Main(string[] args){Console.WriteLine("handle=" + mre.Handle.ToString("x"));for (int i = 0; i < 100; i++){mre.Reset();Console.WriteLine($"{i}:当前为阻塞模式,请观察");Console.ReadLine();mre.Set();Console.WriteLine($"{i}:当前为畅通模式,请观察");Console.ReadLine();}Console.ReadLine();}}
为了找到 =23c 所对应的内核地址,可以借助工具,截图如下:
接下来启动双机调试,看下内核态上 ffffe 内存位置的内容 。
0: kd> dp 0xFFFFE00155522220 L1ffffe001`5552222000000000`00060000
在控制台上将设为有信号模式,再次观察这块内存 。

C# 中的那些锁,在内核态都是怎么保证同步的?

文章插图
1: kd> dp 0xFFFFE00155522220 L1ffffe001`5552222000000001`00060000
大家可以仔细试试看,会发现 ffffe+0x4 的位置一直都是 0,1 之间的切换,可以推测此时是一个 bool 类型 。
有些朋友很好奇,能不能观察看到它的调用栈呢?肯定是可以的,我们使用 ba 下一个硬件断点,观察下它的用户态和内核态栈 。
1: kd> ba w4 0xFFFFE00155522220+0x41: kd> gBreakpoint 0 hitnt!KeResetEvent+0x32:fffff802`f8c3e752 f081237ffffffflock and dword ptr [rbx],0FFFFFF7Fh0: kd> k# Child-SPRetAddrCall Site00 ffffd000`ac0cea90 fffff802`f910ebd0nt!KeResetEvent+0x3201 ffffd000`ac0ceac0 fffff802`f8d59b63nt!NtClearEvent+0x5002 ffffd000`ac0ceb00 00007fff`d8963c0ant!KiSystemServiceCopyEnd+0x1303 000000c9`10ece4d8 00007fff`d5e0057antdll!NtClearEvent+0xa04 000000c9`10ece4e0 00007fff`b88fba05KERNELBASE!ResetEvent+0xa05 000000c9`10ece510 00000000`00000000System_Private_CoreLib!System.Boolean Interop+Kernel32::ResetEvent(Microsoft.Win32.SafeHandles.SafeWaitHandle)$##60000B0+0x65...