PART 01
可用工作线程 AvailableWorkerThreads 最大工作线程 MaxWorkerThreads 当前活跃工作线程 = MaxWorkerThreads - AvailableWorkerThreads 线程池排队任务队列长度(关键瓶颈指标) 完成任务总数、每秒处理任务速率 线程创建/销毁次数、线程扩容延迟 长时间阻塞线程数量
可用IO线程 AvailableIOThreads 最大IO线程 MaxIOThreads IO活跃线程数 IO完成回调排队量
UnobservedTaskException 未观测异步异常次数 任务平均排队耗时(队列堆积代表性能瓶颈) GC STW停顿(GC会阻塞所有线程池工作) 线程池线程平均空闲时长
队列长度持续 > 0:线程不足,任务排队; 活跃Worker持续接近MaxWorkerThreads:大量同步阻塞,线程池耗尽; 频繁创建新线程(500ms扩容一次):MinThreads配置过低; UnobservedTaskException突增:大量失火任务,隐藏业务异常。
PART 02// 获取当前可用、最大 Worker / IO 线程 ThreadPool.GetAvailableThreads(out int availWorker, out int availIo); ThreadPool.GetMaxThreads(out int maxWorker, out int maxIo); ThreadPool.GetMinThreads(out int minWorker, out int minIo); int activeWorker = maxWorker - availWorker; int activeIo = maxIo - availIo; Console.WriteLine($1quot;【线程池监控】"); Console.WriteLine($1quot;Worker:活跃={activeWorker} 可用={availWorker} 最大={maxWorker} 最小={minWorker}"); Console.WriteLine($1quot;IOCP:活跃={activeIo} 可用={availIo} 最大={maxIo}");long pending = ThreadPool.PendingWorkItemCount; long completed = ThreadPool.CompletedWorkItemCount; Console.WriteLine($1quot;排队任务数:{pending} 累计完成:{completed}");
PendingWorkItemCount > 0 持续上涨 = 线程池处理不过来,出现排队阻塞。
PART 03// 程序入口注册一次 TaskScheduler.UnobservedTaskException += (sender, args) => { // 上报监控、日志告警 Console.WriteLine($1quot;未观测异步异常:{args.Exception}"); args.SetObserved(); // 标记已观测,防止进程崩溃 };// BackgroundService 中定时采集线程池指标上报监控系统 async Task CollectThreadPoolMetric(CancellationToken token) { using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5)); while (await timer.WaitForNextTickAsync(token)) { ThreadPool.GetAvailableThreads(out int availW, out int availIO); ThreadPool.GetMaxThreads(out int maxW, out int maxIO); long pending = ThreadPool.PendingWorkItemCount; long complete = ThreadPool.CompletedWorkItemCount; // 上报Prometheus/日志/监控平台 ReportMetric("threadpool.worker.active", maxW - availW); ReportMetric("threadpool.io.active", maxIO - availIO); ReportMetric("threadpool.pending.count", pending); ReportMetric("threadpool.completed.total", complete); // 告警判断:队列积压超过阈值 if (pending > 100) { SendAlert("线程池任务队列大量积压!"); } } }
Threads in use:当前正在使用的Worker线程 Queue Length:线程池排队任务总数(最核心) Completed Work Items:累计完成任务 Unobserved Task Exceptions:未观测异常数量 Thread Count:线程池总线程数 Min Threads / Max Threads:配置阈值
Win+R 输入 perfmon 打开性能监视器; 右侧图表右键「添加计数器」; 选择 .NET CLR ThreadPool,勾选需要指标,选择目标进程; 实时曲线查看队列堆积、线程占用。
PART 04
查看所有线程池Worker线程ID; 切换线程查看调用堆栈,定位长期阻塞的代码(Result/Wait/Sleep/长同步锁)。
调试 → 性能探查器; 勾选「CPU 使用情况」「.NET 对象分配」; 运行程序,查看:
线程池线程长时间占用的函数; 大量Task创建导致GC压力; 同步阻塞调用栈。
VS打开Dump文件,查看所有线程堆栈; 批量筛选处于阻塞状态的Worker线程,定位阻塞代码。
PART 05
threadpool-worker-thread-count:活跃Worker线程 threadpool-io-thread-count:活跃IO线程 threadpool-pending-work-item-count:排队任务数 threadpool-completed-work-item-count:完成任务速率 threadpool-adjustment-interval-ms:线程扩容间隔(默认500ms)
自建监听 EventListener 采集指标上报监控; 使用 dotnet-counters 命令行实时查看; Prometheus + OpenTelemetry 持久化、绘图、告警。
dotnet tool install -g dotnet-counters
# 进程ID替换为目标程序PID dotnet-counters monitor -p 1234 System.Threading.ThreadPool
System.Threading.ThreadPool threadpool-worker-thread-count: 12 threadpool-io-thread-count: 4 threadpool-pending-work-item-count: 0 threadpool-completed-work-item-count: 1240
threadpool-pending-work-item-count > 50 持续10s → 线程池队列积压告警; threadpool-worker-thread-count >= MaxWorkerThreads - 5 → 线程即将耗尽告警; unobserved_task_exception_total > 0 → 异步失火异常告警。
PART 06
dotnet-counters 跨平台通用; gcore 抓取进程Dump,配合 lldb + sos 查看线程堆栈; htop 查看进程总线程数,判断线程是否疯狂扩容; perf 采样CPU,定位长时间占用Worker的同步计算代码。
PART 07
PART 08
基础埋点:程序内定时采集 PendingWorkItemCount、活跃Worker/IO线程,上报时序数据库; 全局异常监控:注册 UnobservedTaskException 捕获隐藏异步报错; 线上实时排查:服务器使用 dotnet-counters 快速查看线程池状态; 长期可视化:OpenTelemetry采集CLR EventCounter,Prometheus绘图; 配置告警:队列积压、线程接近上限、未观测异常三类核心告警; 故障留证:线程池指标异常时自动抓取进程Dump,留存堆栈定位阻塞代码。
PART 09
代码埋点:业务程序内采集基础指标,用于实时上报告警; 命令行工具 dotnet-counters:线上临时快速排查; 系统性能计数器 / EventCounter:CLR底层原生指标,无侵入; Dump/VS调试:线下定位阻塞线程调用栈。