为什么要看线程状态
进程级视图只能告诉你「谁吃 CPU」「谁没响应」,说不清原因:一个进程可能大部分线程在等锁、只有一个线程在空转。线程级排查就是往下钻一层——看线程数量、CPU 时间排序、以及每个线程处于什么状态、在等什么资源。CPU 高的线程去看调用栈,长时间无响应的进程去看等待链,这样才能区分「忙」和「卡住」两类截然不同的问题。
基本信息
| 适用平台 | Windows 全版本;线程对象对所有进程通用 |
|---|---|
| PowerShell 取数 | (Get-Process -Name 进程名).Threads 枚举线程对象 |
| 可读字段 | Id 线程号、ThreadState 状态、WaitReason 等待原因、TotalProcessorTime 累计 CPU 时间、StartTime 启动时间 |
| 图形工具 | Process Explorer 进程属性里的 Threads 页,可看调用栈与等待链 |
| 托管代码专用 | dotnet-stack、dotnet-dump、dotnet-trace 查看托管线程栈 |
| 权限要求 | 查看系统进程线程通常需要管理员并启用调试权限 |
典型使用场景
- 无响应排查:进程界面卡死不退出,判断卡在哪个等待上
- 高 CPU 定位:找出 CPU 时间最高的线程,再看它执行的模块与函数
- 死锁分析:用等待链视图发现两个资源互等的环
- 线程泄漏:线程数持续增长说明有代码没有释放线程,需看创建位置
- 服务卡顿:业务超时但进程不忙,检查是否线程池耗尽或长期等待外部资源
常用示例
常用示例
$p = Get-Process -Name MyApp # 取进程对象
$p.Threads.Count # 线程总数
$p.Threads | Sort-Object TotalProcessorTime -Descending |
Select-Object -First 5 Id, ThreadState, WaitReason, TotalProcessorTime # CPU 时间最高的 5 个线程
$p.Threads | Group-Object ThreadState | Select-Object Name, Count # 状态分布,快速判断是否大面积等待
$p.Threads | Where-Object WaitReason -ne 'Executive' | Select-Object Id, ThreadState, WaitReason # 非普通等待
Get-Process -Name MyApp | Select-Object Id, Threads.Count, CPU, WS # 线程数与资源对照状态与等待原因读法
| 取值 | 含义 | 排查提示 |
|---|---|---|
| Running | 正在使用处理器 | 采到 Running 的线程值得看调用栈 |
| Wait | 等待某个对象 | 配合 WaitReason 判断等的是什么 |
| Ready | 就绪但没轮到 CPU | 大面积 Ready 说明 CPU 是瓶颈 |
| Executive | 等待内核对象(常见默认值) | 多数线程长期处于此状态属正常 |
| UserRequest | 等待用户态请求(如消息队列) | 界面类进程长时间无响应时常见 |
| Suspended | 被挂起 | 人为挂起或调试场景,长时间挂起需确认来源 |
ThreadState 与 WaitReason 是采样瞬间的快照,同一次取值里同一个线程的状态可能下一秒就变了。判断趋势要隔几秒多抓几次,或者用性能计数器看更长时间窗口。
托管(.NET)死锁要看托管栈:原生线程栈常常只显示等待在一个内核对象上,看不出业务原因,这时用 dotnet-stack、dotnet-dump 抓托管栈更直接。栈的可读性还依赖符号文件,配置符号服务器后模块与函数名才会显示。
图形化与托管工具配合
图形化与托管工具配合
REM Process Explorer(需管理员)
REM 双击进程 -> Threads 页:看 Start Address(归属模块)、CPU、State、Wait Reason
REM 选中可疑线程点 Stack 按钮:看调用栈(配置符号后函数名可读)
REM 右键进程 -> Properties -> 检查等待链(Wait Chain):显示等待环
REM 任务管理器 -> 详细信息 -> 右键进程 -> 分析等待链
REM .NET 应用的托管栈
REM dotnet-stack report -p <PID>
REM dotnet-dump collect -p <PID> 然后 dotnet-dump analyze 查看线程抓取进程转储(dump)会让目标进程在抓取期间短暂暂停,高负载生产系统上可能触发超时或断路器。抓之前确认业务可承受,并优先在单机或副本环境复现。