发布于2026-07-18 阅读(0)
扫一扫,手机访问
实际开发中,性能和代码质量是每个开发者都在追求的目标。而在C#里,Stopwatch计时器几乎是性能测量的标配工具。但很多人在使用过程中会遇到各种奇怪的问题,比如明明执行了代码,却返回0毫秒,或者在不同场景下结果不一致。今天就把这些坑和正确用法说清楚。
不少开发者习惯先new Stopwatch(),再手动调用Start()。但说实话,这个习惯挺危险——一不小心就忘了调Start(),结果ElapsedMilliseconds永远返回0。调试时一看,sw.IsRunning一直是false,才恍然大悟。
静态方法Stopwatch.StartNew()内部已经完成了实例化、启动和计数器可用性检测,语义明确,零遗漏风险。所以,直接写var sw = Stopwatch.StartNew();就对了。
另外注意,.NET Framework 4.5+才支持Restart(),老版本只能用Reset()加Start()的组合。
这两者区别在哪?ElapsedMilliseconds返回的是long类型的整数毫秒,所有小数部分会被直接截断。而Elapsed.TotalMilliseconds返回的是double类型,能精确到微秒级——比如12.345678毫秒这样的值都能完整保留。
对于性能敏感的场景,比如算法对比、基准测试,这个差异就很要命了。举个例子:循环执行100次某操作,单次耗时大约0.8毫秒。如果用ElapsedMilliseconds,全部显示为0;而Elapsed.TotalMilliseconds能稳定给出0.792~0.815这样的数值。
所以,应用场景要分清楚:
sw.Elapsed.TotalMillisecondssw.ElapsedMilliseconds就够了sw.ElapsedTicks,再除以Stopwatch.FrequencyStopwatch不会自动停下来。如果你没调Stop()就去读Elapsed,拿到的其实是当前运行时间——它还在跑着呢。更麻烦的是,如果在未停止的状态下又调了一次Start(),就会抛出InvalidOperationException。
容易踩的坑:在while循环里反复调Start()却没调Stop(),或者误以为Reset()会自动停止计时器(它不会)。
正确做法很明确:
StartNew() → 执行代码 → Stop()sw.Restart()(推荐)或sw.Reset(); sw.Start();Console.WriteLine(sw.IsRunning);,能快速确认状态Stopwatch底层依赖的是系统高性能计数器,在Windows上就是QueryPerformanceCounter。它不受系统时间调整的影响,也不和DateTime.UtcNow对齐。它的任务只有一个:回答“这段代码跑了多久”,而不是“它是什么时候跑的”。
如果你需要把耗时和日志时间戳关联起来,比如排查某次请求从收到到响应花了多久,得额外记一次DateTime.UtcNow。但计时本身绝不能靠DateTime算差值——DateTime.Now可能因为NTP同步而跳变,绝对不可用于差值计算。
所以,千万别写这种代码:var start = DateTime.UtcNow; ... var elapsed = (DateTime.UtcNow - start).TotalMilliseconds;
正确组合应该是:
var startLogTime = DateTime.UtcNow;
var sw = Stopwatch.StartNew();
// ... 执行代码 ...
sw.Stop();
Console.WriteLine($"{startLogTime:O} | {sw.Elapsed.TotalMilliseconds:F3}ms");
跨进程或高并发场景下,Stopwatch依然可靠。实际项目中最容易被忽略的是IsRunning状态检查和TotalMilliseconds的精度取舍——尤其在压测或微基准测试时,差一个数量级的误差,往往就出在这里。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8