如何理解 .NET 线程池
写 ASP.NET Core 时大多数人关注的是业务逻辑浏览器发起请求Controller处理最后返回 JSON。中间件、Filter、依赖注入DI这些内容大家也都比较熟悉但还有一个容易被忽略的问题这段代码到底运行在哪个线程上线程是谁创建的await数据库查询时线程去哪了为什么 CPU 和内存都不高请求响应却越来越慢这些问题其实都和 ASP.NET Core 的底层执行模型有关。这篇文章就沿着一次 HTTP 请求的生命周期把 Kestrel、线程池、async/await 以及线程池饥饿串起来讲清楚。上一篇聊的是 async 为什么不会一直占着线程这一篇则继续回答另一个问题线程从哪里来又为什么会不够用。HTTP 请求是谁执行的直觉里的链路浏览器 → Controller → 返回实际少了一步。Kestrel 收到请求后不会自己执行业务代码而是把处理工作交给 CLR 的调度中心——我们常说的线程池Thread Pool浏览器 ↓ Kestrel 收到 HTTP ↓ 线程池取出一名工人Worker Thread ↓ Middleware 管道 ↓ Controller / Action ↓ 返回 Response工人回池里待命Controller 并不是自己在跑。是线程池里的某条工作线程正在执行你的 Middleware 和 Action。这一点想通后面 async 为什么能提并发、.Result为什么会拖垮服务都有落脚点。先把线程池想成一支施工队不必一上来就背ThreadPool、Hill Climbing这些词。可以先把 CLR 线程池想成工地上的施工队活来了HTTP 请求、Task.Run、定时器回调…… ↓ 先排队 ↓ 哪个工人闲着哪个上 ↓ 干完不回家回到队里等下一单这名工人就是ThreadPool Worker Thread。施工队不必每个请求现招一个人new Thread()招人、辞退都贵每人还要占一块栈内存默认大约 1MB。预先养一队人复用才撑得住 Web 的高并发。关键限制整个进程通常只有这一支施工队。HTTP 请求、Task.Run、很多定时器回调默认都往同一个池子里排。一个模块把工人占光或堵住别的模块也得干等。跟完一次请求同步写法[HttpGet({id})]publicIActionResultGet(intid){varorder_db.Orders.Find(id);// 同步查库returnOk(order);}HTTP 请求到达 ↓ Kestrel ↓ 线程池 Worker #12 接单 ↓ Middleware → Controller.Get ↓ Worker #12 发起 SQL原地阻塞等数据库 ↓ 等待期间 #12 什么别的活也干不了 ↓ 数据返回Worker #12 继续写出 Response ↓ Worker #12 回池并发 500 个这样的同步请求就可能需要 500 条线程同时堵在等数据库上——施工队很快不够用新请求只能在门口排队。await 时工人去哪了换成 asyncpublicasyncTaskIActionResultGet(intid){varorderawait_db.Orders.FindAsync(id);returnOk(order);}HTTP 请求到达 ↓ 线程池 Worker #12 接单 ↓ Middleware → Controller.Get ↓ 执行到 await FindAsyncSQL 交给数据库Worker #12 释放回池 ↓ #12 可以去处理别的 HTTP 请求了 ↓ 数据库完成通过 I/O 完成端口IOCP通知运行时 ↓ 线程池再派一名工人可能是 #12也可能是 #27执行 await 之后的代码 ↓ 返回 Responseasync 没有消灭线程池而是让工人在等 I/O 的时候不必傻站着。几十个工人就能轮转几百个挂起中的请求。这和 async 那篇是同一套故事的两面那边讲 Task 与挂起这边讲工人从池里来、用完还回去。为什么线程会不够施工队的人数不是固定的CLR 会根据负载调节但有上限。线程不够通常不是机器核数太少而是工人被占住不还publicIActionResultGet(){returnOk(_service.GetDataAsync().Result);// 占着工人等 Task}工人在池子线程上调用.Result/.Wait()等于占着岗位干等。并发一上来空闲工人归零新请求只能排队——表现就是延迟慢慢变大CPU 却不高。线上真正把服务拖慢的往往是大量请求在池子线程上同步等待.Result、.GetAwaiter().GetResult()Thread.Sleep同步HttpClient、StreamReader.ReadToEnd()以为用了异步其实把所有逻辑塞进Task.Run和 HTTP 请求抢同一批工人ThreadPool Starvation系统为什么越来越慢ThreadPool Starvation线程池饥饿就是任务还在源源不断地来但长时间拿不到空闲工人。门口排队的活越积越多大部分请求还行偶尔有一批特别慢而 CPU 利用率并不夸张。几种典型场面在工人线程上同步阻塞 I/OvardataGetDataAsync().Result;一条线程被占住就少一条线程服务其他请求。大量 CPU 密集活丢进池子awaitTask.Run(()HeavyCompute());Task.Run会从施工队里领一名工人专门跑这段计算计算没完之前工人不还。请求一多工人都去算数了等 I/O 的请求排成长队。在池子线程里再等池子线程awaitTask.Run(()otherTask.Wait());工人 A 等工人 BB 可能也在等 A——死锁式饥饿。排查时看阻塞栈、线程池计数器、dotnet-trace先找代码里的同步等待而不是先调配置。施工队怎么补人Work Stealing 与 Hill Climbing工人不够时CLR 会尝试加人但不会无限加——有MaxThreads上限到了就只能排队。Work Stealing为什么池子调度快除了复用线程.NET 线程池还有一个关键机制Work Stealing工作窃取。每个 Worker 有自己的本地队列活来了优先塞给刚交活的那个工人减少锁竞争。本队列为空时工人会去别的 Worker 队列尾部偷任务Worker1 ── Queue1 Worker2 ── Queue2 Worker3 ── Queue3 ↓ Worker3 闲下来了 ↓ 从 Queue1 尾部 Steal 一个任务这是 TPLTask.Run、Parallel.For在线程池上效率高的原因之一不是只有一个全局大队列大家抢。Hill Climbing加线程的依据CLR 内部用Hill Climbing等策略根据吞吐量反馈慢慢调节工人数量在MinThreads和MaxThreads之间找平衡。不必深究算法细节记住用途即可池子自己决定何时加人、何时减人开发者日常不用手算该开几条线程。Task.Run 不是异步是换一名工人干同步活很多人以为Task.Run和GetAsync是一类东西——不是。awaitTask.Run(()Compute());Task.Run ↓ 从线程池领一名工人 ↓ 这名工人同步执行 Compute() ↓ 执行完工人回池Task.Run没有创造 I/O 异步。它只是把一段同步工作交给施工队里的另一名工人去做本质上是线程切换不是 IOCP 那套非阻塞等待。源码路径可以简化为Task.Run(...) ↓ Task.Factory.StartNew / InternalStartNew ↓ TaskScheduler.Default ↓ ThreadPoolTaskScheduler ← Default 就是它 ↓ ThreadPool.UnsafeQueueUserWorkItem所以TaskScheduler.Default不是什么神秘调度器就是ThreadPoolTaskScheduler最终排队进同一个 CLR 线程池。I/O 用GetAsync/FindAsyncCPU 密集或改不了的阻塞 API才在边界上用Task.Run隔离。长时间占线的活后台监听、消费循环可以用TaskCreationOptions.LongRunning提示单独开线程避免占死池子里的工人Task.Factory.StartNew(LongLoop,TaskCreationOptions.LongRunning);为什么微软不建议你调线程池ThreadPool.SetMinThreads、SetMaxThreads能调但文档和实战经验都指向同一件事绝大多数时候问题在代码不在池子配置。99% 的饥饿是工人被同步阻塞占住不是 MinThreads 少了一个零.Result.Wait()Thread.Sleep 同步 HttpClient先把 API 改成全链路 async去掉阻塞等待比调参数管用得多。只有监控明确显示刚启动或扩容后短时间排队明显、且代码侧已排查过才考虑适度调高MinThreads。SetMinThreads不是立刻创建那么多人调太高还会浪费空闲线程的栈内存。SetMaxThreads更要慎动限制不当等于亲手制造排队。一个请求完整经历了什么把这篇和 async、Middleware 放在一起一条链是这样的HTTP 请求 ↓ Kestrel ↓ 线程池派出 Worker ↓ Middleware 管道异常、路由、认证…… ↓ Controller Action ↓ await SqlConnection / EF / HttpClient ↓ Worker 释放回池async I/O 等待 ↓ 数据库 / 网络完成IOCP 通知运行时 ↓ 线程池再派 Worker 执行续体 ↓ Result → Formatter → Response ↓ Worker 回池等待下一单若其中某处写成.Result或同步 I/OWorker 就会在原地阻塞施工队被占满后面的请求只能在门口等——这就是线程池视角下的性能事故。一张表记住线程池概念要点ThreadPool Worker执行 Controller、Middleware、Task.Run 的工作线程进程内单池HTTP、Task.Run、多数回调共用同一 CLR 线程池async I/O等待期间释放 Worker工人可去接别的请求ThreadPool Starvation工人被占满新任务长时间排队Work Stealing本地队列 偷任务降低调度开销Hill ClimbingCLR 自动调节工人数有 Min/Max 边界Task.Run领一名 Worker 跑同步代码不是 I/O 异步TaskScheduler.Default即 ThreadPoolTaskScheduler最容易搞混的几个问题问题一句话回答Controller 跑在哪线程池 Worker 上不是 Kestrel 自己跑业务代码。await 时线程去哪了回线程池I/O 完成后池子再派 Worker 跑续体。每个 Task 有独立池吗没有进程内共用一个 CLR 线程池。Task.Run是异步吗不是是把同步工作交给池里另一名 Worker。TaskScheduler.Default是什么ThreadPoolTaskScheduler底层进线程池队列。饥饿常见原因池子线程上.Result、同步 I/O、CPU 活占满工人。Work Stealing 解决什么多 Worker 本地队列 偷任务提高调度效率。该不该调SetMinThreads先修阻塞代码仅刚启动后排队明显、且监控证实时才考虑。官方文档Thread pool

相关新闻

提示词创意发散的“第二大脑”已上线:融合认知科学×大语言模型的6维发散评估框架(附Python自动化打分脚本)

提示词创意发散的“第二大脑”已上线:融合认知科学×大语言模型的6维发散评估框架(附Python自动化打分脚本)

更多请点击: https://intelliparadigm.com 第一章:提示词创意发散的“第二大脑”概念演进与认知根基 “第二大脑”并非指代物理意义上的冗余计算单元,而是一种人机协同的认知增强范式——它将大语言模型作为外部记忆延伸、思维缓冲区与创意催…

2026/7/24 21:24:21 阅读更多 →
AI智能体与具身智能关系误区纠偏(3)

AI智能体与具身智能关系误区纠偏(3)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

2026/7/24 21:23:20 阅读更多 →
AI智能体与具身智能关系误区纠偏(2)

AI智能体与具身智能关系误区纠偏(2)

前沿技术探索:AI智能体视觉(TVA,Transformer-based Vision Agent)是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术,是集深度强化学习(DRL)、卷积神经网络(CNN…

2026/7/24 21:23:20 阅读更多 →

最新新闻

18.2腾讯云为了强制使用「静态网站域名」访问,屏蔽了默认域名的直接预览,项目使用别的办法来实现(接18.1改。1的尾)

18.2腾讯云为了强制使用「静态网站域名」访问,屏蔽了默认域名的直接预览,项目使用别的办法来实现(接18.1改。1的尾)

我现在的配置 100% 正确(文件访问权限是公开、强制 HTTPS已经开启、Content-Type 设为了 text/html,编码 UTF-8),但浏览器还是下载,通过查资料再和官方客服工单对接,唯一的原因是:腾讯云为了强制…

2026/7/24 21:32:23 阅读更多 →
Cpp2IL:揭秘Unity IL2CPP黑箱的终极逆向工具指南

Cpp2IL:揭秘Unity IL2CPP黑箱的终极逆向工具指南

Cpp2IL:揭秘Unity IL2CPP黑箱的终极逆向工具指南 【免费下载链接】Cpp2IL Work-in-progress tool to reverse unitys IL2CPP toolchain. 项目地址: https://gitcode.com/gh_mirrors/cp/Cpp2IL 你是否曾面对Unity IL2CPP编译后的GameAssembly.dll感到无从下手…

2026/7/24 21:32:23 阅读更多 →
终极StreamFX完整指南:如何在5分钟内为OBS直播添加专业级特效

终极StreamFX完整指南:如何在5分钟内为OBS直播添加专业级特效

终极StreamFX完整指南:如何在5分钟内为OBS直播添加专业级特效 【免费下载链接】obs-StreamFX StreamFX is a plugin for OBS Studio which adds many new effects, filters, sources, transitions and encoders! Be it 3D Transform, Blur, complex Masking, or eve…

2026/7/24 21:32:23 阅读更多 →
银河麒麟系统V10 SP1(ARM64)平台编译Qt5.15.2+QGroundControl-v4.4

银河麒麟系统V10 SP1(ARM64)平台编译Qt5.15.2+QGroundControl-v4.4

QGroundControl (ARM平台) 编译与部署流程 本文章记录了在麒麟系统 Arm平台上,从源码编译定制版 Qt 5.15.2,直至成功构建并运行 QGroundControl (QGC) 的完整流程。 银河麒麟嵌入式操作系统 V10 SP1 arm64 即 ARMv8-A 64位架构 Ubuntu 20.04(glibc 2.31) 一、 环境准备与…

2026/7/24 21:32:23 阅读更多 →
Kimi K3暂停新用户订阅后,怎么免费体验?看这一篇就够了!(含K3完全使用指南)

Kimi K3暂停新用户订阅后,怎么免费体验?看这一篇就够了!(含K3完全使用指南)

Kimi K3 从发布到限流,这三天发生了什么7月16日晚发布Kimi K3;7月17日腾讯Buddy全系产品(CodeBuddy、WorkBuddy)官宣接入;7月19日晚Kimi(月之暗面)就自己发公告说"算力扛不住了&#xff0c…

2026/7/24 21:32:23 阅读更多 →
Sunshine游戏串流实战指南:构建你的专属云游戏服务器

Sunshine游戏串流实战指南:构建你的专属云游戏服务器

Sunshine游戏串流实战指南:构建你的专属云游戏服务器 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 想要在任何设备上流畅玩PC游戏?Sunshine自托管游戏串流…

2026/7/24 21:31:23 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻