Stopwatch.StartNew() 是最安全的初始化方式,它自动完成实例化和启动,避免因遗漏 Start() 导致 Elapsed 为 0;应复用实例并用 Restart() 分段计时,慎用 ElapsedMilliseconds 防精度丢失。

Stopwatch.StartNew() 是最安全的初始化方式
直接调用 Stopwatch.StartNew(),别先 new 再 Start —— 后者多一步就可能漏掉 Start(),导致 Elapsed 始终为 0。这个静态方法内部自动完成实例化和启动,语义清晰、不易出错。
常见错误现象:stopwatch.Elapsed.TotalMilliseconds 总是返回 0,检查发现忘了调用 Start() 或误调了 Restart()(它只重置不启动)。
-
Stopwatch.StartNew()返回已启动的实例,开箱即用 - 手动 new:
var sw = new Stopwatch(); sw.Start();多一环节,协作时容易被删掉Start() - 不要用
Stopwatch.IsHighResolution判断能否用——它只是说明底层是否支持高精度,不影响StartNew()行为
计时区间必须严格配对 Stop() 和 Restart()
Stopwatch 不是“自动暂停”的秒表;Stop() 后再 Elapsed 就冻结了,想继续测下一段,得用 Restart()(等价于 Stop() + Start()),而不是再次 Start()(重复 Start 会报 InvalidOperationException)。
典型场景:循环中逐次测量每次迭代耗时,或分阶段测「预处理→主计算→后处理」。
- 错误写法:
sw.Start(); DoWork(); sw.Stop(); sw.Start(); DoMore();→ 第二次Start()抛异常 - 正确写法:
sw.Restart(); DoWork(); sw.Stop();或sw.Stop(); ... sw.Start();(中间确保没重复 Start) - 如果只关心总耗时,全程不用 Stop(),最后直接读
Elapsed即可
避免在 Stopwatch 运行时读取 ElapsedMilliseconds 等整型属性
ElapsedMilliseconds 是 long 类型的只读属性,但它会截断毫秒后的小数部分。实际底层计时器精度常达微秒级(尤其 IsHighResolution == true 时),直接用它会丢精度,且在极短时间(
使用场景:算法执行时间通常很短,靠 ElapsedMilliseconds 很难区分 0.2ms 和 0.8ms 的差异。
- 优先用
Elapsed.TotalMilliseconds(double,含小数)或ElapsedTicks(原始计数,无舍入) - 注意
TotalMilliseconds是 double,做相等判断要小心浮点误差;比大小可用> 0.01这类阈值 - 跨平台时,.NET 5+ 在 Windows/Linux/macOS 上都默认启用高精度计时器,无需额外配置
Stopwatch 本身不引发 GC,但别把它塞进热循环里反复 new
Stopwatch 是 struct,栈上分配,没 GC 压力。但频繁调用 Stopwatch.StartNew() 仍会带来微小构造开销,尤其在每毫秒执行多次的热点路径中。
容易被忽略的地方:有人为“线程安全”每个线程都 new 一个,结果对象创建频次远超必要——Stopwatch 实例本就是线程私有的,复用完全没问题。
- 单次测量:用
Stopwatch.StartNew()最省心 - 高频重复测量(如性能压测循环):复用同一个实例,
Restart()比 new 更轻量 - 别为了“避免共享”把 Stopwatch 放进 static 字段——它不是线程安全的共享资源,每个线程该有自己的实例
Stopwatch 的精度取决于系统计时器,Windows 上通常是 10–15ms 的默认分辨率,但 .NET 会自动尝试启用高精度模式(QueryPerformanceCounter)。真正影响结果的,往往不是 Stopwatch 本身,而是你测的那段代码有没有被 JIT 预热、有没有被内联、GC 是否恰好在中间发生——这些比选哪个属性读数更值得盯住。










