记一次 .NET 某新能源系统 线程疯涨 分析

一:背景

1. 讲故事

前段时间收到一个朋友的求助,说他的程序线程数疯涨,寻求如何解决。

09b7e4b1cd48274b8444afe85e520bef.png

等我分析完之后,我觉得这个问题很有代表性,所以拿出来和大家分享下,还是上老工具 WinDbg。

二:WinDbg 分析

1. 线程真的在疯涨吗

要想查线程有没有疯涨,可以用 !t 命令看一下。

0:000:x86> !t
ThreadCount:      382
UnstartedThread:  1
BackgroundThread: 376
PendingThread:    0
DeadThread:       2
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1  59c 00e52fb0     26020 Preemptive  12D67610:00000000 00e4b408 0     STA 2    2 2b30 00e61aa0     2b220 Preemptive  00000000:00000000 00e4b408 0     MTA (Finalizer) 3    3 18cc 00ea72b8   202b220 Preemptive  00000000:00000000 00e4b408 0     MTA 5    4 1f18 00f02998   1020220 Preemptive  00000000:00000000 00e4b408 0     Ukn (Threadpool Worker) 
XXXX    6    0 00f056f8     39820 Preemptive  00000000:00000000 00e4b408 0     MTA 6    7 2154 09052448   202b020 Preemptive  12E353E0:00000000 00e4b408 0     MTA ...377  373 2ee8 21a90958   1029220 Preemptive  12D1FCCC:00000000 00e4b408 0     MTA (Threadpool Worker) 378  374 227c 21b1d510   1029220 Preemptive  12DCBFC8:00000000 00e4b408 0     MTA (Threadpool Worker) 379  375  7e8 21b1baa8   1029220 Preemptive  12D39ADC:00000000 00e4b408 0     MTA (Threadpool Worker) 380  376 1d1c 21a8fec8   1029220 Preemptive  12D11F40:00000000 00e4b408 0     MTA (Threadpool Worker) 381  366 19ec 215c1bd0   1029220 Preemptive  12DB42D8:00000000 00e4b408 0     MTA (Threadpool Worker) 382  377 1dc8 21b1bff0   1029220 Preemptive  12C71F9C:00000000 00e4b408 0     MTA (Threadpool Worker) 383  378  f94 215bc750   1029220 Preemptive  12E10568:00000000 00e4b408 0     MTA (Threadpool Worker) 384  379 17d4 21ac5580   1029220 Preemptive  12D8EE98:00000000 00e4b408 0     MTA (Threadpool Worker) 385  381 2c1c 21b1b018   1029220 Preemptive  12D0DD00:00000000 00e4b408 0     MTA (Threadpool Worker) 386  380 309c 21b1da58   1029220 Preemptive  12E25028:00000000 00e4b408 0     MTA (Threadpool Worker) 387  382 3048 21ac6aa0   1029220 Preemptive  12DFA918:00000000 00e4b408 0     MTA (Threadpool Worker)

从卦中看,主线程是一个 STA,说明是一个窗体程序,一个窗体能做到 387 个线程,也是挺牛的,同时也能观察到大多都是 ThreadPool Worker ,也就是线程池工作线程。

2. 这些线程都在干嘛

这里有一个小技巧,那就是线程号越大,往往都是最新创建的,往往从这里面就能套出来一些有用的东西,言外之意就扒一扒 380 ~ 387 这些线程的调用栈。

0:387:x86> ~387s
ntdll_77380000!NtWaitForSingleObject+0xc:
773f29dc c20c00          ret     0Ch
0:387:x86> k
CvRegToMachine(x86) conversion failure for 0x14f
X86MachineInfo::SetVal: unknown register 0 requested# ChildEBP RetAddr      
00 31fef104 755a1539     ntdll_77380000!NtWaitForSingleObject+0xc
01 31fef104 74b3ee3b     KERNELBASE!WaitForSingleObjectEx+0x99
02 31fef168 74b3efed     clr!CLRSemaphore::Wait+0xbe
03 31fef19c 74b3eee2     clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x13a
04 31fef204 74a54c27     clr!ThreadpoolMgr::WorkerThreadStart+0x328
05 31feff24 7649fa29     clr!Thread::intermediateThreadProc+0x58
06 31feff34 773e7a7e     kernel32!BaseThreadInitThunk+0x19
07 31feff90 773e7a4e     ntdll_77380000!__RtlUserThreadStart+0x2f
0:387:x86> ~386s
ntdll_77380000!NtWaitForSingleObject+0xc:
773f29dc c20c00          ret     0Ch
0:386:x86> k
CvRegToMachine(x86) conversion failure for 0x14f
X86MachineInfo::SetVal: unknown register 0 requested# ChildEBP RetAddr      
00 31d6ede4 755a1539     ntdll_77380000!NtWaitForSingleObject+0xc
01 31d6ede4 74b3ee3b     KERNELBASE!WaitForSingleObjectEx+0x99
02 31d6ee48 74b3efed     clr!CLRSemaphore::Wait+0xbe
03 31d6ee7c 74b3eee2     clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x13a
04 31d6eee4 74a54c27     clr!ThreadpoolMgr::WorkerThreadStart+0x328
05 31d6fb84 7649fa29     clr!Thread::intermediateThreadProc+0x58
06 31d6fb94 773e7a7e     kernel32!BaseThreadInitThunk+0x19
07 31d6fbf0 773e7a4e     ntdll_77380000!__RtlUserThreadStart+0x2f
0:386:x86> ~385s
ntdll_77380000!NtWaitForSingleObject+0xc:
773f29dc c20c00          ret     0Ch
0:385:x86> k
CvRegToMachine(x86) conversion failure for 0x14f
X86MachineInfo::SetVal: unknown register 0 requested# ChildEBP RetAddr      
00 31eaee64 755a1539     ntdll_77380000!NtWaitForSingleObject+0xc
01 31eaee64 74b3ee3b     KERNELBASE!WaitForSingleObjectEx+0x99
02 31eaeec8 74b3efed     clr!CLRSemaphore::Wait+0xbe
03 31eaeefc 74b3eee2     clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x13a
04 31eaef64 74a54c27     clr!ThreadpoolMgr::WorkerThreadStart+0x328
05 31eafb7c 7649fa29     clr!Thread::intermediateThreadProc+0x58
06 31eafb8c 773e7a7e     kernel32!BaseThreadInitThunk+0x19
07 31eafbe8 773e7a4e     ntdll_77380000!__RtlUserThreadStart+0x2f

从线程栈上看,这些线程都在 UnfairSemaphore 处等待,这是一个正常现象,因为这些线程都是通过 UnfairSemaphore 锁来唤醒,不过奇怪的是,这些线程为什么产生,又为什么不被消亡?

根据经验预测:肯定有代码在不断的调度 线程池 线程,然后又做了一个短命的操作,导致线程池线程不断新增,又得不到线程可以被消亡的阈值。

3. 程序真的在频繁调度线程吗

既然猜测是程序在频繁的调用线程池线程,能做的只能是观察此时 dump 中的所有线程的线程栈,看能不能挖到点有价值的东西,可以使用 ~*e !clrstack 命令。

c436f056f0cfba665580eb63a4a2ba28.png

经过仔细观察这近400个线程栈,发现有 37 处都是 System.Threading.Thread.Sleep(Int32), 而且大多都是 HslCommunication.Core.Net.NetworkBase.ThreadPoolCheckTimeOut(System.Object) 函数,能清楚的看到是由线程池发起的,接下来就是用 ILSpy 反编译下这个函数看下到底是怎么回事。

protected void ThreadPoolCheckTimeOut(object obj){HslTimeOut hslTimeOut;if ((hslTimeOut = obj as HslTimeOut) == null){return;}while (!hslTimeOut.IsSuccessful){Thread.Sleep(100);if ((DateTime.Now - hslTimeOut.StartTime).TotalMilliseconds > (double)hslTimeOut.DelayTime){if (!hslTimeOut.IsSuccessful){LogNet?.WriteWarn(ToString(), "Wait Time Out : " + hslTimeOut.DelayTime);hslTimeOut.Operator?.Invoke();hslTimeOut.WorkSocket?.Close();}break;}}}

接下来通过 ILSpy 查看这个方法的引用,发现有很多处,抽几个如下:

protected OperateResult<TNetMessage> ReceiveMessage<TNetMessage>(Socket socket, int timeOut, TNetMessage netMsg) where TNetMessage : INetMessage{...if (timeOut > 0){ThreadPool.QueueUserWorkItem(ThreadPoolCheckTimeOut, hslTimeOut);}...}protected OperateResult<Socket> CreateSocketAndConnect(IPEndPoint endPoint, int timeOut){...ThreadPool.QueueUserWorkItem(ThreadPoolCheckTimeOut, hslTimeOut);...}protected void CreateSocketAndConnect(IPEndPoint endPoint, int timeOut, Action<OperateResult<Socket>> connectCallback){...ThreadPool.QueueUserWorkItem(ThreadPoolCheckTimeOut, hslTimeOut);...}

从上面代码看,确实存在一些商榷的地方,很多的 socket 操作都用线程池来处理 ThreadPoolCheckTimeOut() 函数,而在这个函数内当 hslTimeOut.IsSuccessful =false 的时候,在 if ((DateTime.Now - hslTimeOut.StartTime).TotalMilliseconds > (double)hslTimeOut.DelayTime) 不满足的时间区间内会一直 sleep,这就导致当 socket 请求量上去后,导致很多线程处于 sleep 状态, 线程池又不得不生成更多的线程来处理 ThreadPoolCheckTimeOut() 逻辑。

到这里终于就找到了符合 线程池线程 疯涨的底层逻辑,接下来看看 HslCommunication.dll 为何物,去找一下它的类库声明。

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("HslCommunication")]
[assembly: AssemblyDescription("一个框架库,包含完善的网络通信及日志组件")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HslCommunication")]
[assembly: AssemblyCopyright("Copyright © By Richard.Hu 2018")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d3710b78-1b32-4d53-9604-0451a795a2f5")]
[assembly: AssemblyFileVersion("5.3.2.0")]
[assembly: AssemblyVersion("5.3.2.0")]

可以看到,这是一个商业组件。

80683a61901fab15d395e5be5ff6cf30.png

三:总结

由于定位到疑似是 HslCommunication 组件的问题,看了下还是 商业版 , 这就尴尬了,建议的解决办法如下:

1) 短期:

ThreadPool.SetMaxThreads 限定线程上限。

2) 长期:

找作者看看有没有最新版,或者到 https://github.com/dathlin/HslCommunication 上提一个 issue,让别人系统性解决一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]ES7、ES8、ES9、ES10新特性大盘点

ES7、ES8、ES9、ES10新特性大盘点 本文转自&#xff1a;https://mp.weixin.qq.com/s/8bov6788ivV0sHzmwrn5lw 以下文章来源于前端工匠 &#xff0c;作者浪里行舟君 前端工匠 我是浪里行舟&#xff0c;Github博客4000star作者&#xff0c;致力于打造一系列能够帮助初中级工程师…

热榜!!!数据结构与算法:C语言版---数组与稀疏矩阵---强势来袭!

数组是各种计算机语言中经常使用到的重要数据结构&#xff0c;一般的说&#xff1a;在内存中申请一片连续地址的存储空间、存储这些数、就称为数组。 在C语言中&#xff0c;申请连续的存储空间是很容易的事情&#xff0c;但难在多维数组的组织、以及数组数据的压缩上&#xff…

第一声问候

前一篇《Emacs 是一台计算机》理解了 Emacs 身为计算机的本质之后&#xff0c;在 Emacs 里编程就顺理成章了。不过&#xff0c;在此之前&#xff0c;还需要略微介绍一下 Emacs 最基本的操作。 系统的不一致&#xff0c;令人有点烦躁 现在&#xff0c;也可以坦然地说&#xff0c…

破解支付宝AR红包

支付宝新出的AR红包没多久&#xff0c;就有人破解了&#xff0c;大致原理是将上面的像素条遮挡下面的黑条&#xff0c;基本上得到模糊的图就可以扫到红包。不过现在大多是ps解决&#xff0c;那得有多麻烦啊&#xff0c;所以我用java写了一个&#xff0c;效果还不错。 先截屏&am…

在 Windows 上搭建配置 Jenkins 然后编译打包 VS 项目

在 Windows 上搭建配置 Jenkins 然后编译打包 VS 项目独立观察员 2022 年 7 月 6 日一、安装1、下载并安装 JRE &#xff08;Java 运行环境&#xff09;。2、下载 Windows 版本的 Jenkins 安装包并安装。3、安装 Visual Studio&#xff0c;以供编译项目使用。4、安装 Advanced …

【ArcGIS微课1000例】0007:基于数字高程模型DEM生成剖面线、剖面图

文章目录 效果预览数据分析工具介绍生成过程剖面图编辑保存、导出剖面图实验数据下载效果预览 数据分析 本实例使用到的原始数据为案例提供的规则格网DEM

[转]javaandroid线程池

java多线程-概念&创建启动&中断&守护线程&优先级&线程状态&#xff08;多线程编程之一&#xff09;java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()&#xff08;多线程编程之二&#xff09;java&android线程池-Exe…

.Net下极限生产力之efcore分表分库全自动化迁移CodeFirst

开始本次我们的主题就是极限生产力,其他语言望尘莫及的分表分库全自动化Migrations Code-First 加 efcore 分表分库无感开发还记得上次发布博客还是在上次,上次发布了如何兼容WTM框架后也有不少小伙伴来问我如何兼容如何迁移等问题,经过这么多框架的兼容我自己也认识到了一些问…

Hadoop日常管理与维护

本文描述了hadoop、hbase的启动关闭、表操作以及权限管理。一、Hadoop服务的启动与关闭1、启动使用hadoop以及hbase自带的脚本进行启动&#xff0c;先启动hadoop个服务&#xff0c;再启动hbase服务。 hadoopbdi:~$ start-dfs.sh hadoopbdi:~$ start-yarn.sh hadoopbdi:~$ start…

Mathematica修改默认字体

1. 打开Option Inspector 2. 第一个下拉框选择Global Preference, 搜索stylehints 3. 修改字体为想要换的字体FamilyName, 比如换成苹果黑体 SimHei, 字体FamilyName自行研究 4. 效果 转载于:https://www.cnblogs.com/dabaopku/p/6221960.html

基于JavaScript 数组的经典程序应用源码(强烈建议收藏)

文章目录设计一个数组输入并显示的程序。数组输入和显示选择排序选择排序排序程序包排序网页杨辉三角形杨辉三角形网页C语言画一个sin(x)的曲线螺旋线访问二维数组JavaScript数组的定义、使用都是非常简单的&#xff0c;仅仅定义的话&#xff0c;就使用&#xff1a; var anew …

【ArcGIS微课1000例】0008:ArcGIS中如何设置相对路径?(解决图层前红色的感叹号)

文章目录 问题举例问题分析解决办法问题举例 我们在使用ArcGIS时经常会碰到这样的问题:将地图文档(.mxd)保存到本地并拷贝到别的电脑上或改变一个路径时,出现数据丢失的现象,具体表现为图层前面出现一个红色的感叹号,如下图所示。 出现以上问题的根本原因是数据GSS.tif的…

[转]OKR结合CFR的管理模式

读前预 无论任何管理书籍&#xff0c;都是围绕着人性&#xff0c;如果激发员工的人性中的自尊和自我价值观、自我成就感。 作为一名领导者&#xff0c;在管理前&#xff0c;必须要是冷静&#xff0c;安静的对待他人 约翰杜尔为谷歌送上大礼 “好主意”再加上”卓越的执行”&…

NetCore 国际化最佳实践

NetCore 国际化最佳实践ASP.NET Core中提供了一些本地化服务和中间件&#xff0c;可将网站本地化为不同的语言文化。ASP.NET Core中我们可以使用Microsoft.AspNetCore.Localization库来实现本地化。但是默认只支持使用资源文件方式做多语言存储&#xff0c;很难在实际场景中使用…

复分析——第1章——复分析准备知识(E.M. Stein R. Shakarchi)

第一章 复分析准备知识 (Preliminaries to Complex Analysis) The sweeping development of mathematics during the last two centuries is due in large part to the introduction of complex numbers; paradoxically, this is based on the seemingly absurd no…

【ArcGIS微课1000例】0009:ArcGIS影像拼接(镶嵌、镶嵌至新栅格)

本课程以 DEM数据为例,讲述ArcGIS中影像的拼接方法及注意事项。 文章目录 方法一:Mosaic工具方法二:Mosaic To New Raster工具实验数据下载观察两个数据,接边处切合效果很好。 方法一:Mosaic工具 使用Mosaic工具要千万注意,Mosaic的结果是将输入栅格追加到目标栅格上,…

C语言试题170之矩阵转置

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目:设有一矩…

【ArcGIS微课1000例】0010:ArcGIS影像裁剪(裁剪、掩膜提取)

文章目录 裁剪方法方法一:Extract By Mask(按掩膜提取)方法二:Clip(裁剪)数据下载裁剪方法 方法一:Extract By Mask(按掩膜提取) 加载配套的实验数据,运行Extract By Mask(按掩膜提取)工具,参数设置如下: 掩膜提取结果: 方法二:Clip(裁剪) 加载配套的实验…

阿里创新自动化测试工具平台--Doom

背景 信息系统上线后通常会需要迭代升级甚至重构&#xff0c;如何确保系统原有业务的正确性非常重要。曾经有一家叫瑞穗证券的证券公司因为一个系统bug导致了数亿美金的损失&#xff0c;赔掉了公司一年的利润。这样的极端例子虽然少见&#xff0c;但是却像达摩克利斯之剑警示着…

《微信读书》自定义样式

一直用微信读书看书&#xff0c;但是微信读书的Web版的布局不太喜欢。 重写下它的样式再加上单击关闭工具栏&#xff0c;这样看着舒服多了^_^&#xff1a; /*浮动工具栏*/ document.getElementsByClassName("readerControls")[0].style.left"0"; document.…