记一次 .NET 某工厂无人车调度系统 线程爆高分析
一:背景
1. 讲故事
前些天有位朋友找到我,说他程序中的线程数爆高,让我帮忙看下怎么回事,这种线程数爆高的情况找问题相对比较容易,就让朋友丢一个dump给我,看看便知。
二:为什么会爆高
1. 查看托管线程
别人说的话不一定是真,得自己拿数据出来说话,可以用 !t 命令观察一下便知。
0:000> !t
ThreadCount: 4683
UnstartedThread: 0
BackgroundThread: 4663
PendingThread: 0
DeadThread: 19
Hosted Runtime: noLock DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception0 1 cc44 00000268048778C0 202a020 Preemptive 0000000000000000:0000000000000000 00000268048c6d50 -00001 MTA ...
4670 4679 51bc 0000026D143F0420 302b220 Preemptive 0000000000000000:0000000000000000 00000268048c6d50 -00001 MTA (Threadpool Worker)
4671 4680 3a68 0000026D143F52E0 302b220 Preemptive 0000000000000000:0000000000000000 00000268048c6d50 -00001 MTA (Threadpool Worker)
4672 4681 337c 0000026D143F1140 302b220 Preemptive 0000026A88AAF5B8:0000026A88AB08D0 00000268048c6d50 -00001 MTA (Threadpool Worker)
4673 4682 188d4 0000026D143F0AB0 302b220 Preemptive 000002698881A760:000002698881C0B8 00000268048c6d50 -00001 MTA (Threadpool Worker)
4674 4683 4bcc 0000026D143EF700 302b220 Preemptive 0000026B889C4488:0000026B889C5E18 00000268048c6d50 -00001 MTA (Threadpool Worker)
从卦中信息看确实有 4600+ 的线程,说明确实存在问题,接下来用 ~*e !clrstack 观察每一个线程都在做什么,线程太多没法全部输出完毕,不过很容易的看到有大量的线程卡在 RoutingService.Push 上,截图如下:

接下来就是观察下这个 Push 方法的逻辑,发现卡死在 Result 上,整理后的代码大概如下:
private readonly SemaphoreSlim slim = new SemaphoreSlim(1, 1);public void Push(string xxx, xxx xxx)
{int num = (xxx.Serial = GetSerial().Result);
}private async Task<int> GetSerial()
{await slim.WaitAsync();try{Interlocked.Increment(ref serial);}finally{slim.Release();}return serial;
}
上面的代码看起来挺奇葩的,为什么 GetSerial() 中不直接用 Interlocked.Increment() 呢?套一个 SemaphoreSlim 显得非常多余。
先不管多余不多余,既然 Result 得不到值,就说明这个异步方法得不到完成,那为什么得不到完成呢?
2. 为什么异步得不到完成
熟悉 SemaphoreSlim.WaitAsync() 的朋友应该知道,这里涉及不到异步IO,所以这个是假异步,本质上就是动态生成了一个串联的 Task<bool>,要想知道得不到完成的根本原因,还得要挖一挖此时的 slim 信号量情况。
0:000> !do 000002690664b5a0
Name: System.Threading.SemaphoreSlim
MethodTable: 00007ff894e56fc0
EEClass: 00007ff894e3f230
Tracked Type: false
Size: 64(0x40) bytes
File: D:\xxx\System.Private.CoreLib.dll
Fields:MT Field Offset Type VT Attr Value Name
00007ff8948094b0 4000c2e 28 System.Int32 1 instance 0 m_currentCount
00007ff8948094b0 4000c2f 2c System.Int32 1 instance 1 m_maxCount
00007ff8948094b0 4000c30 30 System.Int32 1 instance 0 m_waitCount
00007ff8948094b0 4000c31 34 System.Int32 1 instance 0 m_countOfWaitersPulsedToWake
00007ff8962871e0 4000c32 8 ...Private.CoreLib]] 0 instance 000002690664b5e0 m_lockObjAndDisposed
00007ff894e555f0 4000c33 10 ....ManualResetEvent 0 instance 0000000000000000 m_waitHandle
00007ff894e57870 4000c34 18 ...horeSlim+TaskNode 0 instance 0000026b86919a30 m_asyncHead
00007ff894e57870 4000c35 20 ...horeSlim+TaskNode 0 instance 0000026b889c4378 m_asyncTail
00007ff894a4a1f0 4000c36 888 ...Private.CoreLib]] 0 static 00000268864f83a0 s_cancellationTokenCanceledEventHandler
从卦中看当前的 m_currentCount=0,表明当前的信号量被消费完了,所以其他的线程都在等待就能很好理解,接下来的问题是那个从 1->0 的持有线程为什么不归还? 这个就比较难搞了,可以从如下两个思路思考:
- 观察 Result
首先怀疑是不是 Result 引发的死锁,用 !eeversion 看了下是 asp.net core ,并没有所谓的同步上下文,所以这个问题不存在。
0:000> !eeversion
6.0.2023.32017 free
6,0,2023,32017 @Commit: a08d9ce2caf02455c0b825bcdc32974bdf769a80
Server mode with 8 gc heaps
SOS Version: 7.0.8.30602 retail build
- 观察代码
因为 SemaphoreSlim 并不记录持有线程,windbg 在这里就起不到很好的效果,不过仔细阅读代码,发现应该将 await slim.WaitAsync(); 放到 try 中更合理一点,否则无法保证 WaitAsync 和 Release 一定是成双成对的,截图如下:

3. 什么时候开始阻塞的
仔细观察这个 GetSerial 方法,看看里面的 serial 值就知道大概是进行到哪一步才出的问题。
0:4674> !DumpObj /d 000002690664b258
Name: xxx.RoutingService
MethodTable: 00007ff895283ed0
EEClass: 00007ff89526ae08
Tracked Type: false
Size: 112(0x70) bytes
File: D:\xxx\xxx.dll
Fields:MT Field Offset Type VT Attr Value Name
...
00007ff894e56fc0 4000214 48 ...ing.SemaphoreSlim 0 instance 000002690664b5a0 slim
00007ff8948094b0 4000215 60 System.Int32 1 instance 9061 serial
从卦中看已经自增到了 9061 ,然后因为某种原因导致wait 和 release 不匹配了,像这种情况线程池也会有大量的任务积压,可以用 !tp 观察下。
0:4674> !tp
logStart: 33
logSize: 200
CPU utilization: 22 %
Worker Thread: Total: 4652 Running: 4652 Idle: 0 MaxLimit: 32767 MinLimit: 8
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 2 Free: 2 MaxFree: 16 CurrentLimit: 2 MaxLimit: 1000 MinLimit: 8
细心的朋友会发现这里的 Work Request in Queue: 0 ,既然是 0 何来积压?其实这是 sos 的bug,我们需要自己到线程池队列中提取,从当前的线程栈上寻找 ThreadPoolWorkQueue 对象即可。
0:4674> !dso
OS Thread Id: 0x4bcc (4674)
000000EF384FF5C8 0000026b06544848 System.Threading.ThreadPoolWorkQueue0:4674> !DumpObj /d 0000026b06544848
Name: System.Threading.ThreadPoolWorkQueue
MethodTable: 00007ff894e59d80
EEClass: 00007ff894ee01d0
Tracked Type: false
Size: 168(0xa8) bytes
File: D:\xxx\System.Private.CoreLib.dll
Fields:MT Field Offset Type VT Attr Value Name
00007ff89476bf38 4000c61 18 System.Boolean 1 instance 0 loggingEnabled
00007ff89476bf38 4000c62 19 System.Boolean 1 instance 0 _dispatchTimeSensitiveWorkFirst
00007ff89637fc20 4000c63 8 ...Private.CoreLib]] 0 instance 0000026b065448f0 workItems
00007ff89637fe00 4000c64 10 ...Private.CoreLib]] 0 instance 0000026b06544930 timeSensitiveWorkQueue
00007ff894e59d10 4000c65 20 ...acheLineSeparated 1 instance 0000026b06544868 _separated0:4674> !ext dcq 0000026b065448f0
System.Collections.Concurrent.ConcurrentQueue<System.Object>1 - dumpobj 0x0000026806c782f8
...
119419 - dumpobj 0x000002690a097658
119420 - dumpobj 0x000002690a097810
119421 - dumpobj 0x000002690a0981a8
---------------------------------------------
119421 items
从卦中可以看到大概有12w的积压。上面就是我的完整分析思路,最后就是告诉朋友最好的办法就是去掉多余累赘的 SemaphoreSlim ,直接用同步的方式执行 Interlocked.Increment(ref serial) 即可,简单粗暴。
三:总结
这次线程爆高的事故原因还是挺有意思的,用了一个双同步来获取 serial 值,感觉像是一种聪明反被聪明误,代码一定要简单粗暴,代码越少bug越少。
相关文章:
记一次 .NET 某工厂无人车调度系统 线程爆高分析
一:背景 1. 讲故事 前些天有位朋友找到我,说他程序中的线程数爆高,让我帮忙看下怎么回事,这种线程数爆高的情况找问题相对比较容易,就让朋友丢一个dump给我,看看便知。 二:为什么会爆高 1. …...
高等数学啃书汇总重难点(九)多元函数微分法及其应用
下册最重要也是个人认为偏恶心的一节(主要东西是真不少....)重点在于会计算偏导、能理解全微分及隐函数求导3个核心内容,至于后面的关于几何层面的应用,建议掌握计算方法即可,学有余力再死磕推导过程等内容~ 1.平面点集…...
Vue3前端100个必要的知识点
为什么是必要的,就是这100个知识点学完后,能独立完成一个小项目。最终能得到一个解决方案。也算是前端知识的积累。如果后面有需要的地方可以回来查。100个其实比较多,我会按新手老鸟,大神来分成3个等级,话不多说&…...
CCS3列表和超链接样式
在默认状态下,超链接文本显示为蓝色、下画线效果,当鼠标指针移过超链接时显示为手形,访问过的超链接文本显示为紫色;而列表项目默认会缩进显示,并在左侧显示项目符号。在网页设计中,一般可以根据需要重新定…...
vue手机项目如何控制蓝牙连接
要控制蓝牙连接,您需要使用Vue的蓝牙插件或库,例如BLE-Peripheral或cordova-plugin-ble-central。以下是一些基本步骤: 导入蓝牙插件或库。在Vue组件中创建一个蓝牙对象并初始化它。扫描周围的蓝牙设备并连接到所需的设备。一旦连接成功&…...
遥遥领先,免费开源的django4-vue3项目
星域后台管理系统前端介绍 🌿项目简介 本项目前端基于当下流行且常用的vue3作为主要技术栈进行开发,融合了typescript和element-plus-ui,提供暗黑模式和白昼模式两种主题以及全屏切换,开发bug少,简单易学,…...
视频平台跨网级联视频压缩解决方案
一、 简介 视频监控领域对带宽有着较大的需求,这是因为视频流需要实时占用网络带宽资源。视频监控的传输带宽是组网结构的基础保障,关系到视频监控的稳定性、可靠性和可拓展性等因素。例如,720P的视频格式每路摄像头的比特率为2Mbpsÿ…...
利用python进行数据分析 pdf
利用python进行数据分析 pdf 介绍 在现代社会中,随着大数据时代的到来,数据分析的需求越来越大。而Python作为一门简洁且易于学习的编程语言,具有强大的数据分析能力,成为了广大数据分析师的首选工具之一。本文将指导一位刚入行的…...
Day46.算法训练
518. 零钱兑换 II class Solution {public int change(int amount, int[] coins) {int dp[] new int[amount 1];dp[0] 1;// 遍历物品for (int i 0; i < coins.length; i) {// 遍历背包 从小到大for (int j coins[i]; j < amount; j) {dp[j] dp[j - coins[i]]; …...
基于YOLOv8模型暗夜下人脸目标检测系统(PyTorch+Pyside6+YOLOv8模型)
摘要:基于YOLOv8模型暗夜下人脸目标检测系统可用于日常生活中检测与定位黑夜下人脸目标,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法…...
如何在 Photoshop 中使用位图模式制作自定义音乐海报
如何在 Photoshop 中使用位图创建炫酷的音乐海报设计。 1.如何设置新的 Photoshop 文件 步骤1 在 Photoshop中,转到 “文件”>“新建”。将文档命名为 “音乐海报”。 将宽度设置 为 1270 px , 高度 设置为 1600 px。将分辨率 设置 为 72 像素/英寸…...
1 — NLP 的文本预处理技术
一、说明 在本文中,我们将讨论以下主题:1为什么文本预处理很重要?2 文本预处理技术。这个文对预处理做一个完整化、程序化处理,这对NLP处理项目中有很大参考性。 二、为什么文本预处理很重要? 数据质量显着影响机器学习…...
TypeScript之泛型
一、是什么 泛型程序设计(generic programming)是程序设计语言的一种风格或范式 泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型 在typescript中,定义函数,…...
一个小妙招从Prompt菜鸟秒变专家!加州大学提出PromptAgent,帮你高效使用ChatGPT!
夕小瑶科技说 原创 作者 | 谢年年、王二狗 有了ChatGPT、GPT4之后,我们的工作学习效率得到大大提升(特别在凑字数方面୧(๑•̀◡•́๑)૭)。 作为一个工具,有人觉得好用,自然也有人觉得难用。 要把大模型用得6&am…...
Netty通信框架
Netty框架的底层是NIO,NIO:non-blocking io 非阻塞IO 一个线程可以处理多个通道,减少线程创建数量; 读写非阻塞,节约资源:没有可读/可写数据时,不会发生阻塞导致线程资源的浪费 一…...
6西格玛质量标准: 提升业务效率的关键
在现代竞争激烈的商业环境中,企业需要不断提高效率,降低成本,同时确保产品和服务的质量。为了达到这个目标,许多企业已经转向了6西格玛质量标准。这个方法旨在通过最小化缺陷和提高流程稳定性来优化业务运作,为客户提供…...
OpenGL ES相关库加载3D 车辆模型
需求类似奇瑞的这个效果,就是能全方位旋转拖拽看车,以及点击开关车门车窗后备箱等 瑞虎9全景看车 (chery.cn) 最开始收到这个需求的时候还有点无所适从,因为以前没有做过类似的效果,后面一经搜索后发现实现的方式五花八门…...
云原生环境下JAVA应用容器JVM内存如何配置?—— 筑梦之路
Docker环境下的JVM参数非定值配置 —— 筑梦之路_docker jvm设置-CSDN博客 之前简单地记录过一篇,这里在之前的基础上更加细化一下。 场景说明 使用Java开发且设置的JVM堆空间过小时,程序会出现系统内存不足OOM(Out of Memory)的…...
防雷接地测试方法完整方案
防雷接地是保障电力系统、电子设备和建筑物安全的重要措施,防雷接地测试是检验防雷接地装置是否合格的必要手段。本文介绍了防雷接地测试的原理、方法和注意事项,以及如何编写防雷接地测试报告。 地凯科技防雷接地测试的原理 防雷接地测试的基本原理是…...
【云原生-K8s】Kubernetes安全组件CIS基准kube-beach安装及使用
基础介绍kube-beach介绍kube-beach 下载百度网盘下载wget下载 kube-beach安装kube-beach使用基础参数示例结果说明 基础介绍 为了保证集群以及容器应用的安全,Kubernetes 提供了多种安全机制,限制容器的行为,减少容器和集群的攻击面…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
