这篇文章,我们研究一下如何调试一个 死锁问题,可以下载一下 https://github.com/dotnet/samples/tree/main/core/diagnostics/DiagnosticScenarios 源码,程序运行后,你会发现api无响应而且线程会不断增长,然后你会学习到用不同的工具去分析这个死锁问题。

生成 core dump

为了能找出程序无响应的原因,core dump 提供了当时线程的状态以及各种存在线程竞争的锁,接下来运行如下命令:

dotnet run

为了能找到 进程ID,使用如下命令。

dotnet-trace ps

可以自己查看一下 shell 输出,我们的进程是 4807,你的进程号可能不一样,接下来访问URL: https://localhost:5001/api/diagscenario/deadlock ,你会发现这个api请求会一直得不到response,等 10-15s 后,使用下面的命令收集一个 dump 文件。

dotnet-dump collect -p 4807

分析 core dump

为了能够对生成好的 dump 进行分析,使用如下命令 dotnet-dump analyze 命令,参考如下:

dotnet-dump analyze  ~/.dotnet/tools/core_20190513_143916

因为是一个莫名的无响应问题,所以你可能想先对进程中的所有线程有一个整体的感知。

> threads
*0 0x1DBFF (121855)1 0x1DC01 (121857)2 0x1DC02 (121858)3 0x1DC03 (121859)4 0x1DC04 (121860)5 0x1DC05 (121861)6 0x1DC06 (121862)7 0x1DC07 (121863)8 0x1DC08 (121864)9 0x1DC09 (121865)10 0x1DC0A (121866)11 0x1DC0D (121869)12 0x1DC0E (121870)13 0x1DC10 (121872)14 0x1DC11 (121873)15 0x1DC12 (121874)16 0x1DC13 (121875)17 0x1DC14 (121876)18 0x1DC15 (121877)19 0x1DC1C (121884)20 0x1DC1D (121885)21 0x1DC1E (121886)22 0x1DC21 (121889)23 0x1DC22 (121890)24 0x1DC23 (121891)25 0x1DC24 (121892)26 0x1DC25 (121893)
...
...317 0x1DD48 (122184)318 0x1DD49 (122185)319 0x1DD4A (122186)320 0x1DD4B (122187)321 0x1DD4C (122188)

输出中展示了进程中的所有线程ID,以及相关的 调试器线程ID操作系统线程ID

接下来我们有必要了解下每个线程都在做什么?可以用 clrstack 查看线程栈,为了能查看所有的线程栈,使用如下命令:

clrstack -all.........Child SP               IP Call Site
00007F2AE37B5680 00007f305abc6360 [GCFrame: 00007f2ae37b5680]
00007F2AE37B5770 00007f305abc6360 [GCFrame: 00007f2ae37b5770]
00007F2AE37B57D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae37b57d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE37B5920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE37B5950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE37B5CA0 00007f30593044af [GCFrame: 00007f2ae37b5ca0]
00007F2AE37B5D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae37b5d70]
OS Thread Id: 0x1dc82Child SP               IP Call Site
00007F2AE2FB4680 00007f305abc6360 [GCFrame: 00007f2ae2fb4680]
00007F2AE2FB4770 00007f305abc6360 [GCFrame: 00007f2ae2fb4770]
00007F2AE2FB47D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae2fb47d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE2FB4920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE2FB4950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE2FB4CA0 00007f30593044af [GCFrame: 00007f2ae2fb4ca0]
00007F2AE2FB4D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae2fb4d70]
OS Thread Id: 0x1dc83Child SP               IP Call Site
00007F2AE27B3680 00007f305abc6360 [GCFrame: 00007f2ae27b3680]
00007F2AE27B3770 00007f305abc6360 [GCFrame: 00007f2ae27b3770]
00007F2AE27B37D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae27b37d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE27B3920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE27B3950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE27B3CA0 00007f30593044af [GCFrame: 00007f2ae27b3ca0]
00007F2AE27B3D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae27b3d70]
OS Thread Id: 0x1dc84Child SP               IP Call Site
00007F2AE1FB2680 00007f305abc6360 [GCFrame: 00007f2ae1fb2680]
00007F2AE1FB2770 00007f305abc6360 [GCFrame: 00007f2ae1fb2770]
00007F2AE1FB27D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae1fb27d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE1FB2920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE1FB2950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE1FB2CA0 00007f30593044af [GCFrame: 00007f2ae1fb2ca0]
00007F2AE1FB2D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae1fb2d70]
OS Thread Id: 0x1dc85Child SP               IP Call Site
00007F2AE17B1680 00007f305abc6360 [GCFrame: 00007f2ae17b1680]
00007F2AE17B1770 00007f305abc6360 [GCFrame: 00007f2ae17b1770]
00007F2AE17B17D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae17b17d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE17B1920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE17B1950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE17B1CA0 00007f30593044af [GCFrame: 00007f2ae17b1ca0]
00007F2AE17B1D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae17b1d70]
OS Thread Id: 0x1dc86Child SP               IP Call Site
00007F2AE0FB0680 00007f305abc6360 [GCFrame: 00007f2ae0fb0680]
00007F2AE0FB0770 00007f305abc6360 [GCFrame: 00007f2ae0fb0770]
00007F2AE0FB07D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae0fb07d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE0FB0920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE0FB0950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE0FB0CA0 00007f30593044af [GCFrame: 00007f2ae0fb0ca0]
00007F2AE0FB0D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae0fb0d70]
OS Thread Id: 0x1dc87Child SP               IP Call Site
00007F2AE07AF680 00007f305abc6360 [GCFrame: 00007f2ae07af680]
00007F2AE07AF770 00007f305abc6360 [GCFrame: 00007f2ae07af770]
00007F2AE07AF7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae07af7d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE07AF920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE07AF950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE07AFCA0 00007f30593044af [GCFrame: 00007f2ae07afca0]
00007F2AE07AFD70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae07afd70]
OS Thread Id: 0x1dc88Child SP               IP Call Site
00007F2ADFFAE680 00007f305abc6360 [GCFrame: 00007f2adffae680]
00007F2ADFFAE770 00007f305abc6360 [GCFrame: 00007f2adffae770]
00007F2ADFFAE7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2adffae7d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2ADFFAE920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2ADFFAE950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2ADFFAECA0 00007f30593044af [GCFrame: 00007f2adffaeca0]
00007F2ADFFAED70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2adffaed70]
...
...
Observing the callstacks for all 300+ threads shows a pattern where a majority of the threads share a common callstack:ConsoleCopy
OS Thread Id: 0x1dc88Child SP               IP Call Site
00007F2ADFFAE680 00007f305abc6360 [GCFrame: 00007f2adffae680]
00007F2ADFFAE770 00007f305abc6360 [GCFrame: 00007f2adffae770]
00007F2ADFFAE7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2adffae7d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2ADFFAE920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2ADFFAE950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2ADFFAECA0 00007f30593044af [GCFrame: 00007f2adffaeca0]
00007F2ADFFAED70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2adffaed70]

观察这超300+ 的线程栈,发现很多线程都是类似下面的线程栈。

OS Thread Id: 0x1dc88Child SP               IP Call Site
00007F2ADFFAE680 00007f305abc6360 [GCFrame: 00007f2adffae680]
00007F2ADFFAE770 00007f305abc6360 [GCFrame: 00007f2adffae770]
00007F2ADFFAE7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2adffae7d0] System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2ADFFAE920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1() [/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2ADFFAE950 00007F2FE392B46D System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2ADFFAECA0 00007f30593044af [GCFrame: 00007f2adffaeca0]
00007F2ADFFAED70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2adffaed70]

从调用栈看,api请求已经进去了 deadlock 方法,然后在 Monitor.ReliableEnter 处卡住,这就说明有其他线程已经进去了这个临界区

接下来我们要寻找到底是哪一个线程正在持有锁?可以用 syncblk 来获取更多线程锁信息。

> syncblk
Index         SyncBlock MonitorHeld Recursion Owning Thread Info          SyncBlock Owner43 00000246E51268B8          603         1 0000024B713F4E30 5634  28   00000249654b14c0 System.Object44 00000246E5126908            3         1 0000024B713F47E0 51d4  29   00000249654b14d8 System.Object
-----------------------------
Total           344
CCW             1
RCW             2
ComClassFactory 0
Free            0

这里有两个列需要注意下:

  1. MonitorHeld

它记录了这个持有锁当前有多少 持有线程等待线程

  1. Owning Thread Info

它表示当前持有锁的线程信息。

到这里,我们看到有两个线程 0x56340x51d4 持有了一个锁,接下来可以切到各自线程看看里面到底做了什么?可以用 setthreadclrstack 命令。

首先看一下第一个线程 0x5634,我们发现它正在获取一个锁,但通过 syncblk 我们知道这个线程已经持有了一个锁,这问题就大了,可以想象所有等待这个持有锁的线程将会被无限期等待。

> setthread 28
> clrstack
OS Thread Id: 0x5634 (28)Child SP               IP Call Site
0000004E46AFEAA8 00007fff43a5cbc4 [GCFrame: 0000004e46afeaa8]
0000004E46AFEC18 00007fff43a5cbc4 [GCFrame: 0000004e46afec18]
0000004E46AFEC68 00007fff43a5cbc4 [HelperMethodFrame_1OBJ: 0000004e46afec68] System.Threading.Monitor.Enter(System.Object)
0000004E46AFEDC0 00007FFE5EAF9C80 testwebapi.Controllers.DiagScenarioController.DeadlockFunc() [C:\Users\dapine\Downloads\Diagnostic_scenarios_sample_debug_target\Controllers\DiagnosticScenarios.cs @ 58]
0000004E46AFEE30 00007FFE5EAF9B8C testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_0() [C:\Users\dapine\Downloads\Diagnostic_scenarios_sample_debug_target\Controllers\DiagnosticScenarios.cs @ 26]
0000004E46AFEE80 00007FFEBB251A5B System.Threading.ThreadHelper.ThreadStart_Context(System.Object) [/_/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 44]
0000004E46AFEEB0 00007FFE5EAEEEEC System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
0000004E46AFEF20 00007FFEBB234EAB System.Threading.ThreadHelper.ThreadStart() [/_/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 93]
0000004E46AFF138 00007ffebdcc6b63 [GCFrame: 0000004e46aff138]
0000004E46AFF3A0 00007ffebdcc6b63 [DebuggerU2MCatchHandlerFrame: 0000004e46aff3a0]

第二个线程情况类似,剩下的 300+ 线程也差不多都是在这些 死锁 的 lock 上无限期的等待。

.NET Core 中如何调试 死锁 ?相关推荐

  1. 在 .NET Core 中使用 ViewConfig 调试配置

    介绍 .NET Core 中的配置包含了多个配置提供程序,包括了 appsettings.json,环境变量,命令行参数等,还有一些扩展的自定义提供程序,比如说 ApolloConfig,AgileC ...

  2. 你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    你的眼睛背叛你的心:解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题 参考文章: (1)你的眼睛背叛你的心:解决 .NET Co ...

  3. ASP.NET Core中借助CSRedis实现安全高效的分布式锁

    引言 最近回头看了看开发的.NET Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死 ...

  4. 你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?

    起因 在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果: 测试中发现,几乎相同的代码: 在 FineUIMvc(Net Framework)下没有 ...

  5. 一次 .NET Core 中玩锁的经历:ManualResetEventSlim, Semaphore 与 SemaphoreSlim

    最近同事对  .net core memcached 缓存客户端 EnyimMemcachedCore 进行了高并发下的压力测试,发现在 linux 上高并发下使用 async 异步方法读取缓存数据会 ...

  6. 什么是死锁?为什么会死锁?如何解决死锁问题?如何调试死锁问题?

    什么是死锁? 锁,顾名思义,含义真的就是我们平常每天看到的那个锁,锁门的锁,如果门锁着,那就进不去了,那就只能在门外等着. 软件中的锁,意义和这个类似,也是为了阻止非授权用户能够进入某些代码的执行,如 ...

  7. Linux环境崩溃生成core文件以及调试

    Windows环境崩溃问题可根据vs调试工具查看,Linux同样可以查看调用堆栈的信息,只是 需要更改Linux设置,使程序崩溃时候产生core文件.然后gdb调试即可. 1产生core文件方法 产生 ...

  8. NLog在asp.net core中的应用

    Asp.net core中,自带的Log是在当selfhost运行时,在控制台中输出,不便于查阅,如果用一个log架框,把日志持久化,便于查询. NLog是一个免费的日志记录框架,专门为.net平台下 ...

  9. 在 .NET Core 中如何让 Entity Framework Core 在日志中记录由 LINQ 生成的SQL语句

    在开发中,我们想在调试中查看EF Core执行的sql语句,可以使用SQL Studio Manager Tools工具,另一种方式是使用EF Core提供的日志.在ASP.NET Core使用Ent ...

最新文章

  1. PlanAhead与ChipScope
  2. Javascript 面向对象全新理练之数据的封装
  3. RTT设备与驱动之I2C:
  4. python的数值可以转换为字符串_python 数值转换为字符串Python对HTML转义字符进行反转义...
  5. 云拨测助力节卡机器人,全面优化海外网站性能
  6. C++函数中那些不可以被声明为虚函数的函数
  7. c#中Task线程的用法
  8. 站立会议05(第二次冲刺)
  9. 显示多个页面退出登陆_软件测试小白如何第一次登陆时给LINUX的配置网络
  10. 【图像加密】基于matlab混沌算法图像加密解密【含Matlab源码 1218期】
  11. c语言各章知识重点(谭浩强版本)
  12. tiff怎么批量转化成jpg或png?
  13. 十大中文搜索引擎排名,头一个你绝对意想不到!
  14. android 国际化
  15. 微信小程序---简约音乐播放器
  16. 程序员需要学数学吗?
  17. Order Siblings by 排序
  18. 5 开源Math.NET基础数学类库使用 C#解析Delimited Formats数据格式
  19. 纤巧精干:爱普生六轴机器人
  20. [转]英文中的偏旁部首及其记忆

热门文章

  1. 疯狂ios讲义疯狂连载之实现游戏逻辑(2)
  2. centos 6安装报错
  3. “人肉”背后隐藏的网络风险
  4. locate: database too small: /var/db/locate.databas
  5. java wsdl xfire_java调用wsdl xfire和cxf两种方式
  6. 加密文件忘记密码怎么解密_MyBatis 配置文件 用户密码加密存储
  7. Smarty目录结构和子目录路径问题
  8. SQL Server2008导入导出数据库
  9. linux进阶命令2
  10. Oracle数据库IP访问限制(IP白名单黑名单)