dotnet-trace 能否直接定位并发瓶颈?
不能。dotnet-trace 本身不分析线程竞争或锁等待,它只采集底层运行时事件(如
ThreadStart、
ThreadPoolWorkerThreadStart、
MonitorEnter、
ContentionStart)。真正识别“哪个锁卡住了多少线程”,得靠后续用
dotnet-trace convert导出为
nettrace或
speedscope,再结合时间轴和堆栈下钻——关键在采集时是否启用了足够粒度的事件。
必须开启的 trace provider 和参数
默认
dotnet-trace collect不捕获锁争用事件。漏掉
Microsoft-Windows-DotNETRuntime:0x200000000000(即
Contention位),就看不到任何
ContentionStart/
ContentionStop事件,等于白采。 用
--providers显式指定运行时事件:
dotnet-trace collect --process-id 12345 --providers "Microsoft-Windows-DotNETRuntime:0x200000000000,0x80,0x80:0x8000000000000000"其中
0x200000000000是
Contention的 event flag,
0x80是 level(Warning),
0x8000000000000000是 keywords for ThreadPool(可选但推荐) 若要同时看 GC 和 JIT 影响,补上
Microsoft-Windows-DotNETRuntime:0x400000000000,0x4(GC)和
0x100000000000(JIT) 避免用
--profile快捷模式,它默认不包含 Contention 事件
如何从 nettrace 中识别真实并发阻塞点
导出后用
dotnet-trace convert -f speedscope trace.nettrace,在 Speedscope UI 中切到
Flame Graph或
Top Down视图,重点找: 长条状、横向铺开的
ContentionStart事件(不是函数调用,是 runtime 发出的独立事件) 其堆栈中紧邻的上层方法——通常是
Monitor.Enter、
lock(...)对应的 IL 或源码行 多个线程在相同
Monitor对象(看堆栈里
object.GetHashCode()或字段名)上反复出现
ContentionStart→
ContentionStop成对出现,说明存在热点锁 注意区分假阳性:短于 1ms 的争用通常无实际影响;而 >10ms 且高频出现的,大概率是瓶颈
常见误操作与兼容性陷阱
Windows 上用
dotnet-trace分析 .NET 6+ 进程基本稳定,但以下几点极易踩坑: .NET 5 及更早版本不支持
Contention事件 —— 升级 runtime 至少到
.NET 6.0Linux 上需确保内核支持 perf_event,且进程有
perf_event_paranoid权限(常需
sudo sysctl -w kernel.perf_event_paranoid=1) 用
--duration限制采集时间(如
--duration 30s),否则高并发场景下 trace 文件可能达 GB 级,转换失败 不要在生产环境长期启用
Contention事件:它会带来 ~5–10% CPU 开销,仅用于问题复现阶段
真正难的不是采集,而是把
ContentionStart时间戳、线程 ID、锁对象哈希、调用堆栈四者对齐。一次 trace 往往要反复导出、过滤、比对三次以上才能确认根因。
