几年前,我注意到当时 .Net DateTime 类仅以大约 10 到 15 毫秒的增量变化。这恰好与我所理解的 Windows 中线程上下文的典型持续时间相吻合(这就是为什么线程从不休眠的原因)。那里暴露的低分辨率窗口时钟是否真的在线程上下文切换时精确更新?它们可以用作检测上下文切换的廉价而肮脏的方法吗?
2 回答
In fact, I'll go with an actual answer:
Are the low resolution windows clocks exposed there really updated precisely at thread context switches?
No - you've got it backwards. The low-resolution Windows clock MAY precipitate a context-change, eg. if an API call with a timeout times out, or a Sleep() call expires.
Can they be used as a cheap and dirty way to detect context switches?
No, because context-switches may not happen on every timer interrupt and happen often upon all the other hardware interrupts and API calls.
你做了几个无效的或至少没有根据的假设。
首先,您假设 15 毫秒的 .NET 更新DateTime.Now
限制是 Windows 限制。它可能是,但它可能不是。
您假设 的分辨率Thread.Sleep()
与DateTime.Now
. 它不是。您还假设 15 毫秒的最小睡眠时间是 Windows 的限制。它不是。
此外,该声明的最后一部分:
这恰好与我所理解的 Windows 中线程上下文的典型持续时间相吻合(这就是为什么线程从不休眠的原因)。
是完全错误的。一个线程可以放弃其剩余的时间片,这意味着可以安排另一个线程在这段时间内执行。即使在 .NET 程序中,线程也可以休眠少于 15 毫秒。
更重要的是,DateTime.Now
用于进行上下文切换的机制几乎完全不相关。有可能返回的值DateTime.Now
甚至不会更新,除非您请求它,这与Stopwatch
该类不维护Elapsed
字段而是计算您请求时的经过时间的方式大致相同。
所以,不,不可能使用时钟来检测上下文切换。
而且,事实证明,DateTime.Now
15 毫秒分辨率的神话是错误的。该程序表明:
var results = new List<double>();
var startTime = DateTime.Now;
var endTime = startTime.AddSeconds(10);
var lastTime = startTime;
var nowTime = startTime;
while (nowTime < endTime)
{
nowTime = DateTime.Now;
if (nowTime != lastTime)
{
results.Add((nowTime - lastTime).TotalMilliseconds);
lastTime = nowTime;
}
}
Console.WriteLine("Total changes = {0}", results.Count);
var avg = results.Average();
var min = results.Min();
var max = results.Max();
Console.WriteLine("Min: {0}, Max: {1}, Avg: {2}", min, max, avg);
该程序DateTime.Now
在一个紧密的循环中检查 10 秒,并在返回值更改时将一个项目添加到列表中。对我来说,典型的运行结果非常类似于:
Total changes = 9980
Min: 0.9999, Max: 19.998, Avg: 1.00210418837676
所以它在 10,000 毫秒内进行了 9,980 次更新。在我看来,好像只有一次它没有每毫秒更新一次。那可能是因为在那段时间线程被换掉了。
我经常得到:
Total changes = 10000
Min: 1, Max: 1, Avg: 1
这意味着时钟每毫秒更新一次。